原油
最近原油下跌不少,疫情不断,股市惊慌失措。于我而言,管我屁事,没钱。奋(pin)斗(qiong)的我,只有好好写代码。偏题了,是想说缘由
来着,最近越来越觉得github
慢,各种pull,push卡顿,家里打开github网站也巨慢,博客文章打开,各种图裂;现在不止用vscode写代码,还用来写文章,写笔记。所以努力给自己打造一个舒适的写作工具,非常重要。以前都用segmentfault, github来做自己的图片云,可惜免费的始终是有代价的。一直用github issue写文章的我,终于忍不住了,我要换图床。机智的我,去年有活动时,不止买了个3年229的服务器,还话45元买了一个Oss仓库。
vscode 本地图床插件:Picgo
以前都是去github上传了文件,再把地址拷到vscode的文件里,最近想自己写个插件,实现vscode直接上传图片到Oss。一百度,发现自己确实挺落伍,发现个插件Picgo, 我用的阿里云Oss,贴上我个人的vscode配置:
标红的选项很重要。配置完后,截个屏,cmd + option + u 就可以马上感受一下在vscode插图的快感了。
历史文章的图片处理
无法忍受github图片随时图裂,打开缓慢。既然已经有了Oss,那就全部替换了吧,但那么多文章(40+)和笔记(60+),一篇一篇copy,那cmd键估计都按白了吧。懒惰的我,肯定不会,肯定不会用这么土的办法。所以机智的我,基于NodeJs写了一个小工具。
技术栈(Node):fs + http + Oss-Sdk
原理:
- 遍历文章源文件,读取图片地址, 并做标记;
- 下载图片,并上传到Oss,获取新的图片地址;
- 用新的图片地址更新标记位置;
- 更新文件;
代码(涉及到较多正则匹配和骚操作,看不懂的请忽略):
const path = require('path');
const fs = require('fs');
const util = require('util');
const https = require("https");
const http = require("http");
const stream = require('stream');
const Oss = require('./oss');
// NOTE: 方案测试通过;
const isExists = util.promisify(fs.exists);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const reg = /\!\[[\s\S]{3,20}\]\((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?\)/g;
const urlReg = /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/;
const typeReg = /http.*(?=\.[jpg,png,jpeg,gif])/;
const HasReg = /http.*\.(jpg|png|jpeg|gif)$/;
const httpReg = /^http:.+/;
const BUcket_Dir = 'article';
function getRadomName() {
const randomCode = Math.floor(Math.random() * 26) + 65;
return `${Date.now()}-${String.fromCharCode(randomCode)}`;
}
const config = {
accessKeyId: 'your id',
accessKeySecret: 'your secret'
};
const oss = new Oss()
function httpGet(url, enable, check) {
return new Promise((resolve,reject) => {
const method = httpReg.test(url) ? http : https;
method.get(url,
(res) => {
const chunks = [];
let size = 0;
enable && console.log('start');
if (check) {
if (res.statusCode != 200) {
resolve('none');
} else {
resolve(url);
}
return;
}
if (res.statusCode == 301) {
resolve('move');
}
res.on('data', (data) => {
// 收集获取到的文件流
// console.log('trans', data.length);
chunks.push(data);
size += data.length;
});
res.on('end', () => {
// 文件流拼接获取buffer
enable && console.log('end', size);
resolve(Buffer.concat(chunks, size));
});
})
}).catch((error) => {
console.error(error);
});
}
async function getImage({ url, dir }) {
// 判断url 是否带有图片类型标识, 然后生成新的图片地址
let target;
if (url.indexOf('user-images.githubusercontent.com') > 1) {
url = url.replace('https://user-images.githubusercontent.com', 'http://github-production-user-asset-6210df.s3.amazonaws.com');
const arr = url.split('/');
target = `${dir}${arr[arr.length-1]}`;
const ossUrl = `https://doddle.oss-cn-beijing.aliyuncs.com/${target}`;
// 检查文件是否已上传过
const checkExsit = await httpGet(ossUrl, true, true);
if (checkExsit === ossUrl) {
return ossUrl;
}
} else {
target = HasReg.test(url) ?
url.replace(typeReg, `${dir}${getRadomName()}`) :
`${dir}${getRadomName()}.png`;
}
// 获取buffer
const buffer = await httpGet(url, true);
// 发生错误,原地址直接返回,不做替换;
if (!buffer) {
console.log('error happen');
return url;
}
// 如果响应301,则原地址直接返回
if (buffer === 'move') {
return url;
}
// 生成临时文件流, 并将Buffer写入流中
const fileSream = new stream.PassThrough();
fileSream.end(buffer);
// 上传,并获取存储的url
const result = await oss.uploadStream(target, fileSream);
return result.url;
}
async function replaceUrl(file, name) {
const dir = `${BUcket_Dir}/${name ? name : file.replace('.md', '')}/`;
const filePath = path.resolve(__dirname, '../arcticle/', file);
const isExist = await isExists(filePath);
// oss 只初始化一次;
if (!oss.init) {
oss.create(config);
}
// 判断目标文件是否存在;
if(!isExist) {
console.log('file:', file, 'is not exist');
return;
}
// 读取文件内容
const content = await readFile(filePath,'utf8');
let count = 0;
const queue = [];
// 内容替换;
const con = content.replace(reg, (str) => {
// oss 北京的,就不用替换了
if (str.indexOf('doddle.oss-cn-beijing') > 0) {
return str;
}
const url = str.slice(str.indexOf('(') + 1, str.length - 1);
// 占位符
const address = `url-${count}-end`;
queue.push({ url, dir });
// console.log('new', newUrl);
count++;
// 先用站位符替换目标Url,后面再进行下一步;
return str.replace(urlReg, address);
});
// 根据获取原始url,获取对应的Oss url
const res = await Promise.all(queue.map((param) => getImage(param)));
// console.log('count', count, res);
count = 0;
// 根据响应的url数组,替换站位符号
const final = con.replace(/url-[\d]+-end/g, function() {
return res[count++];
});
// 反写文件
await writeFile(filePath, final, {
encoding: 'utf-8'
});
console.log('finish the file:', file);
}
fs.readdir('./arcticle', (err, files) => {
// console.log('file', files.length);
files.forEach((file) => {
// console.log('file', file);
replaceUrl(file, 'shim');
});
})
原理其实很简单,实现也没花多长时间,但还是因为github在国内被墙耽误了不少时间,但我的情况你并不一定遇到,比如:
但机智的我,把https链接换成http,发现图片也能访问,但用Node 发起http请求时,发现响应301,再一看浏览器,发现确实被重定向了。所以就有了下面这段代码:
if (url.indexOf('user-images.githubusercontent.com') > 1) {
url = url.replace('https://user-images.githubusercontent.com', 'http://github-production-user-asset-6210df.s3.amazonaws.com');
}
上面代码注释给的很详细了,看不懂可以留言。
附Oss封装代码:
const OSS = require('ali-oss');
module.exports = class MyOss {
constructor() {
this.client = null;
this.upload = this.upload.bind(this);
this.uploadStream = this.uploadStream.bind(this);
this.getList = this.getList.bind(this);
this.init = false;
}
// 初始化Client
create({ accessKeyId, accessKeySecret }) {
this.client = new OSS({
bucket: 'doddle',
region: 'oss-cn-beijing',
accessKeyId,
accessKeySecret,
secure: true, // 设置为true,返回的url才是https的
});
this.init = true;
}
async upload({ file, key, options = {} }) {
try {
const result = await this.client.put(key, file, options);
return result;
} catch (e) {
console.log(e);
return false;
}
}
async uploadStream(name, file) {
try {
const result = await this.client.putStream(name, file);
return result;
} catch (e) {
console.log(e);
return false;
}
}
async getList(dir = '') {
try {
const result = await this.client.list({
prefix: dir
});
return result;
} catch (e) {
console.error(e);
return false;
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。