背景

由于在本地存了很多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)的方法

  1. // api.js
  2. const axios = require('axios')
  3. class YuqueApi{
  4. constructor() {
  5. this.headers = {
  6. "X-Auth-Token": "<your_token>",
  7. "User-Agent": "<your_UA>"
  8. }
  9. this.params = {}
  10. }
  11. set method(value) {
  12. this.params.method = value
  13. }
  14. get method() {
  15. return this.params.method
  16. }
  17. set uri(value) {
  18. this.params.uri = value
  19. }
  20. get uri() {
  21. return this.params.uri
  22. }
  23. set data(options) {
  24. this.params.options = options
  25. }
  26. get data() {
  27. return this.params.options
  28. }
  29. ajaxPostPromise() {
  30. return axios({
  31. url: this.uri,
  32. method: this.method,
  33. data: this.data,
  34. headers :this.headers
  35. }).then((response) => {
  36. console.log(response.data)
  37. return response.data
  38. })
  39. }
  40. ajaxGetPromise() {
  41. console.log(this.uri)
  42. return axios({
  43. url: this.uri,
  44. method: this.method,
  45. params: this.data,
  46. headers :this.headers
  47. }).then((response) => {
  48. return response.data
  49. })
  50. }
  51. }
  52. module.exports = YuqueApi

在newPromise这个文件里,首先引入api.js里的类文件,并使用map这种的数据结构,存放key值,最后再拼接url,最后导出一个方法,供入口文件(app.js)使用,在此方法中,实例化该对象,并返回调用此方法的返回值

  1. // newPromise.js
  2. const Api = require('./api')
  3. function searchMap(key,slug='') {
  4. let map = new Map()
  5. let baseUri = 'https://www.yuque.com/api/v2'
  6. map.set('USER', '/user')
  7. map.set('REPOS', '/users/da-labs/repos')
  8. map.set('CREATE.DOC', '/repos/da-labs/secnotes/docs')
  9. map.set('GET.DOCS', '/repos/da-labs/secnotes/docs')
  10. map.set('GET.DOC', '/repos/da-labs/secnotes/docs')
  11. return baseUri + map.get(key) + slug
  12. }
  13. module.exports = function askDataToServer({
  14. url = '',
  15. method = 'get',
  16. params = {},
  17. slug = '',
  18. }) {
  19. let api = new Api()
  20. api.uri = searchMap(url,slug)
  21. api.method = method
  22. api.data = params
  23. if (method === 'get')
  24. return api.ajaxGetPromise()
  25. else {
  26. return api.ajaxPostPromise()
  27. }
  28. }

app.js 为 程序入口文件,在uploadImageAndReplace方法中(上传文件,这里是web端语雀,打开某一个文件,上传图片抓的包,这里使用的cookie,Referer,都是该文件的修改的时候抓包得到的,这里web端的cookie比较持久,而且上传的图片是在语雀的图床中,再将其转换为html时,上传到语雀里的时候,语雀就不会把它当作外链图片)

  1. // app.js 入口文件
  2. const http = require('./newPromise')
  3. const fs = require('fs')
  4. const marked = require('marked')
  5. const axios = require('axios')
  6. const FormData = require('form-data')
  7. /*
  8. @params
  9. data = {
  10. title:String,
  11. slug: String (only one),
  12. public: int(0,1)
  13. format: String,
  14. body:String( < 5 MB )
  15. }
  16. */
  17. async function createDoc(data) {
  18. try {
  19. return await http({
  20. url: 'CREATE.DOC',
  21. method: 'post',
  22. params:data
  23. })
  24. } catch (e) {
  25. return 'error'
  26. }
  27. }
  28. /*
  29. @params
  30. filePath : String
  31. @return array
  32. */
  33. function getMarkDownFiles(filePath) {
  34. var children = []
  35. fs.readdirSync(filePath).forEach(function (filename) {
  36. var path = filePath + "/" + filename
  37. var stat = fs.statSync(path)
  38. if (stat && stat.isDirectory()) {
  39. children = children.concat(getMarkDownFiles(path))
  40. }
  41. else {
  42. if (path.indexOf('.md') !== -1) {
  43. children.push(path)
  44. }
  45. }
  46. })
  47. return children
  48. }
  49. /*
  50. @params
  51. markdown:String
  52. @return Promise(markdown)
  53. */
  54. function uploadImageAndReplace(markdown) {
  55. return new Promise((reslove, reject) => {
  56. let file = markdown
  57. fs.readFile(file, encoding='UTF-8', async (err, data) => {
  58. if (err) {
  59. reject(err)
  60. } else {
  61. let oldImage = []
  62. let oldImageChange = []
  63. newData = data.replace(/\r|\n/ig,"")
  64. reg = new RegExp(/!\[.*?\]\((.*?)\)/)
  65. newData = data.match(/!\[.*?\]\((.*?)\)/mg)
  66. for (let i = 0; i < newData.length; i++){
  67. oldImage.push(reg.exec(newData[i])[1])
  68. oldImageChange.push(reg.exec(newData[i])[1].replace(/\\/ig,'/'))
  69. }
  70. let newImage = []
  71. for (let i = 0; i < oldImageChange.length; i++){
  72. imgData = fs.createReadStream(oldImage[i])
  73. let formData = new FormData()
  74. formData.append('file', imgData)
  75. headers = formData.getHeaders()
  76. headers['Cookie'] = '<your_cookie>'
  77. headers['Referer'] = '<your_Referer>'
  78. headers['User-Agent'] = '<your_UA>'
  79. let res = await axios({
  80. url: '<your_url>',
  81. method: 'post',
  82. data: formData,
  83. headers:headers
  84. })
  85. newImage.push(res.data.data.url)
  86. }
  87. for (let i = 0; i < oldImage.length; i++){
  88. data = data.replace(oldImage[i], newImage[i])
  89. }
  90. reslove(data)
  91. }
  92. })
  93. })
  94. }
  95. /*
  96. @params
  97. markdown:String
  98. @return String(html)
  99. */
  100. async function markdownToHtml(markdown) {
  101. return marked.parse(markdown)
  102. }
  103. async function main() {
  104. const filePath = '<your_dir>' // dir 最后结尾不带/
  105. // 读取filepath 目录下所有markdown文档
  106. let arrayFiles = getMarkDownFiles(filePath)
  107. for (let i = 0; i < arrayFiles.length; i++){
  108. // 将markdown文档中的所有图片抽取出来,并上传至图床,最后再将本机图片替换成上传到图床的图片
  109. markdown = await uploadImageAndReplace(arrayFiles[i])
  110. // 将markdown 文档转为html 格式的字符串
  111. const html = await markdownToHtml(markdown)
  112. // 随机生成6位字符串
  113. let slug = Math.random().toString(36).slice(-6)
  114. let title = arrayFiles[i].replace(filePath + '/', '').slice(0, -3)
  115. if (title.indexOf('/') !== -1) {
  116. reg = new RegExp(/\/(.*)/g)
  117. title = reg.exec(title)[1]
  118. }
  119. data = {
  120. title: title,
  121. slug: slug,
  122. public: 1,
  123. format: 'markdown',
  124. body:html
  125. }
  126. // 新增文档
  127. res = await http({
  128. url: 'CREATE.DOC',
  129. method: 'post',
  130. params:data
  131. })
  132. }
  133. }
  134. main()

效果

image.png
image.png

安装说明

  1. # copy 代码
  2. # 然后在该文档中,启动shell,执行命令,下载依赖
  3. npm install
  4. # 修改 app.js 里的filePath
  5. # 修改 app.js 里的<your_cookie>,<your_Referer>,<your_Url>,<your_UA>,这些在抓包语雀的某一个文档上传时可以得到
  6. node app