最近原油下跌很多,疫情不斷,股市惶恐不安。於我而言,管我屁事,沒錢。奮(pin)鬥(qiong)的我,只有好好寫代碼。偏題了,是想說原因
來着,最近愈來愈以爲github
慢,各類pull,push卡頓,家裏打開github網站也巨慢,博客文章打開,各類圖裂;如今不止用vscode寫代碼,還用來寫文章,寫筆記。因此努力給本身打造一個溫馨的寫做工具,很是重要。之前都用segmentfault, github來作本身的圖片雲,惋惜免費的始終是有代價的。一直用github issue寫文章的我,終於忍不住了,我要換圖牀。機智的我,去年有活動時,不止買了個3年229的服務器,還話45元買了一個Oss倉庫。
git
之前都是去github上傳了文件,再把地址拷到vscode的文件裏,最近想本身寫個插件,實現vscode直接上傳圖片到Oss。一百度,發現本身確實挺落伍,發現個插件Picgo, 我用的阿里雲Oss,貼上我我的的vscode配置:
github
標紅的選項很重要。配置完後,截個屏,cmd + option + u 就能夠立刻感覺一下在vscode插圖的快感了。segmentfault
沒法忍受github圖片隨時圖裂,打開緩慢。既然已經有了Oss,那就所有替換了吧,但那麼多文章(40+)和筆記(60+),一篇一篇copy,那cmd鍵估計都按白了吧。懶惰的我,確定不會,確定不會用這麼土的辦法。因此機智的我,基於NodeJs寫了一個小工具。數組
技術棧(Node):fs + http + Oss-Sdk
瀏覽器
原理:服務器
代碼(涉及到較多正則匹配和騷操做,看不懂的請忽略):app
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在國內被牆耽誤了很多時間,但個人狀況你並不必定遇到,好比:
dom
但機智的我,把https連接換成http,發現圖片也能訪問,但用Node 發起http請求時,發現響應301,再一看瀏覽器,發現確實被重定向了。因此就有了下面這段代碼:async
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; } } }