背景
由于在本地存了很多markdown文档,上传到语雀上,特别麻烦,尤其是图片问题,这里我使用nodejs实现将本地markdown上传至语雀本地
为什么不用python?
因为python 的requests 库不支持http2,导致发包过去会报错,服务端不能正常响应
代码设计
api文档:https://www.yuque.com/yuque/developer/api
首先app.js 是封装的一个类,在构造函数里内置header头,里面存放必须的x-auth-token,以及UA头,并封装了get,post(application/json)的方法
// api.jsconst axios = require('axios')class YuqueApi{constructor() {this.headers = {"X-Auth-Token": "<your_token>","User-Agent": "<your_UA>"}this.params = {}}set method(value) {this.params.method = value}get method() {return this.params.method}set uri(value) {this.params.uri = value}get uri() {return this.params.uri}set data(options) {this.params.options = options}get data() {return this.params.options}ajaxPostPromise() {return axios({url: this.uri,method: this.method,data: this.data,headers :this.headers}).then((response) => {console.log(response.data)return response.data})}ajaxGetPromise() {console.log(this.uri)return axios({url: this.uri,method: this.method,params: this.data,headers :this.headers}).then((response) => {return response.data})}}module.exports = YuqueApi
在newPromise这个文件里,首先引入api.js里的类文件,并使用map这种的数据结构,存放key值,最后再拼接url,最后导出一个方法,供入口文件(app.js)使用,在此方法中,实例化该对象,并返回调用此方法的返回值
// newPromise.jsconst Api = require('./api')function searchMap(key,slug='') {let map = new Map()let baseUri = 'https://www.yuque.com/api/v2'map.set('USER', '/user')map.set('REPOS', '/users/da-labs/repos')map.set('CREATE.DOC', '/repos/da-labs/secnotes/docs')map.set('GET.DOCS', '/repos/da-labs/secnotes/docs')map.set('GET.DOC', '/repos/da-labs/secnotes/docs')return baseUri + map.get(key) + slug}module.exports = function askDataToServer({url = '',method = 'get',params = {},slug = '',}) {let api = new Api()api.uri = searchMap(url,slug)api.method = methodapi.data = paramsif (method === 'get')return api.ajaxGetPromise()else {return api.ajaxPostPromise()}}
app.js 为 程序入口文件,在uploadImageAndReplace方法中(上传文件,这里是web端语雀,打开某一个文件,上传图片抓的包,这里使用的cookie,Referer,都是该文件的修改的时候抓包得到的,这里web端的cookie比较持久,而且上传的图片是在语雀的图床中,再将其转换为html时,上传到语雀里的时候,语雀就不会把它当作外链图片)
// app.js 入口文件const http = require('./newPromise')const fs = require('fs')const marked = require('marked')const axios = require('axios')const FormData = require('form-data')/*@paramsdata = {title:String,slug: String (only one),public: int(0,1)format: String,body:String( < 5 MB )}*/async function createDoc(data) {try {return await http({url: 'CREATE.DOC',method: 'post',params:data})} catch (e) {return 'error'}}/*@paramsfilePath : String@return array*/function getMarkDownFiles(filePath) {var children = []fs.readdirSync(filePath).forEach(function (filename) {var path = filePath + "/" + filenamevar stat = fs.statSync(path)if (stat && stat.isDirectory()) {children = children.concat(getMarkDownFiles(path))}else {if (path.indexOf('.md') !== -1) {children.push(path)}}})return children}/*@paramsmarkdown:String@return Promise(markdown)*/function uploadImageAndReplace(markdown) {return new Promise((reslove, reject) => {let file = markdownfs.readFile(file, encoding='UTF-8', async (err, data) => {if (err) {reject(err)} else {let oldImage = []let oldImageChange = []newData = data.replace(/\r|\n/ig,"")reg = new RegExp(/!\[.*?\]\((.*?)\)/)newData = data.match(/!\[.*?\]\((.*?)\)/mg)for (let i = 0; i < newData.length; i++){oldImage.push(reg.exec(newData[i])[1])oldImageChange.push(reg.exec(newData[i])[1].replace(/\\/ig,'/'))}let newImage = []for (let i = 0; i < oldImageChange.length; i++){imgData = fs.createReadStream(oldImage[i])let formData = new FormData()formData.append('file', imgData)headers = formData.getHeaders()headers['Cookie'] = '<your_cookie>'headers['Referer'] = '<your_Referer>'headers['User-Agent'] = '<your_UA>'let res = await axios({url: '<your_url>',method: 'post',data: formData,headers:headers})newImage.push(res.data.data.url)}for (let i = 0; i < oldImage.length; i++){data = data.replace(oldImage[i], newImage[i])}reslove(data)}})})}/*@paramsmarkdown:String@return String(html)*/async function markdownToHtml(markdown) {return marked.parse(markdown)}async function main() {const filePath = '<your_dir>' // dir 最后结尾不带/// 读取filepath 目录下所有markdown文档let arrayFiles = getMarkDownFiles(filePath)for (let i = 0; i < arrayFiles.length; i++){// 将markdown文档中的所有图片抽取出来,并上传至图床,最后再将本机图片替换成上传到图床的图片markdown = await uploadImageAndReplace(arrayFiles[i])// 将markdown 文档转为html 格式的字符串const html = await markdownToHtml(markdown)// 随机生成6位字符串let slug = Math.random().toString(36).slice(-6)let title = arrayFiles[i].replace(filePath + '/', '').slice(0, -3)if (title.indexOf('/') !== -1) {reg = new RegExp(/\/(.*)/g)title = reg.exec(title)[1]}data = {title: title,slug: slug,public: 1,format: 'markdown',body:html}// 新增文档res = await http({url: 'CREATE.DOC',method: 'post',params:data})}}main()
效果
安装说明
# copy 代码# 然后在该文档中,启动shell,执行命令,下载依赖npm install# 修改 app.js 里的filePath# 修改 app.js 里的<your_cookie>,<your_Referer>,<your_Url>,<your_UA>,这些在抓包语雀的某一个文档上传时可以得到node app

