I believe you have encountered this situation when writing a blog with Markdown locally: the article is written, but the image is a local reference path, can not directly copy to the online blog, you need to manually upload.

After suffering from manual uploads, Ben decided to write a small script to free his hands. Scripts rely on static storage of Tencent cloud. No matter what kind of static storage is used, the essence is the same, but the API may be slightly different.

Below, to introduce the automatic upload picture material process:

The following are based on this directory structure ↓↓

Each blog post has its own folder, which contains the blog text and the material used.

① Obtain the Markdown file

const fs = require('fs')
const path = require('path')
// Get instruction parameters
const args = process.argv
const file = args.pop();
const mdPath = path.join(__dirname, `. /${file}`)
const folderPath = mdPath.split('/').slice(0, -1).join('/')
const content = fs.readFileSync(mdPath, { encoding: 'utf-8' })
Copy the code

In this stage, fs and PATH modules are mainly used. Firstly, parameters in the instruction are obtained through process.argv. From the above code, it can be seen that the last parameter of the default instruction of this script is the path and name of Markdown.

Since I have a separate folder for each blog post, I need to split to get the folder path of the blog. Finally, the readFile method is used to read the details in Markdown.

Then it’s time to parse Markdown.

② Parse the Markdown file

There are quite a few plugins to parse Markdown, but I chose the marked one I’ve been using for a while because it’s lightweight and suitable for such small projects.

const marked = require('marked')
const html = marked(content)
Copy the code

There was one question that bothered me while parsing Markdown — when should I filter out the image?

I came up with three plans:

The img tag and the ‘! []()’

② Filter out the tokens related to the picture in the parsing process

Marked does support parsing Markdown into groups of tokens and exporting them.


However, in actual use, the image writing method of img tag is found to be different from Markdown’s native image writing method, and the tokens derived from parsing are different. One is HTML type and the other is Paragraph type, and the Paragraph type is often accompanied by endless nesting. It’s not all that comfortable to handle.

So can we use DOM to recognize images and pull out links like we do in a browser?

Although we are a Node environment, we can actually manipulate the DOM.

Let’s talk about the final implementation scheme.

③ Use JSDOM to obtain the image path

It is not difficult to see jsDOM in the Internet search, front-end students all over the world are the same, want to be able to operate in the Node environment is also familiar with the DOM.

const jsdom = require('jsdom')
const { JSDOM } = jsdom
const dom = new JSDOM(html)
const document = dom.window.document
const imgList = document.querySelectorAll('img')
Copy the code

Convert the HTML string parsed in the previous step into DOM mode, and then querySelectorAll can easily get all the image nodes in the article.

Then upload each picture to complete our automatic upload function.

imgList.forEach((img) = > {
    const src = img.src || ' '
    if(! src ||/http|myqcloud/.test(src)) return
    const imgFullSrc = path.join(folderPath, src)
    const imgName = path.basename(imgFullSrc)
    const imgExt = path.extname(imgName)
    const data = fs.readFileSync(imgFullSrc)
    const md5Name = `${CryptoJS.MD5(data.toString('base64'))}${imgExt}`
    promises.push(uploader(md5Name, data, src))
})
Copy the code

In order to avoid repeated image processing, the script simply added a path judgment, the condition is HTTP and myqcloud, if the image SRC contains these words will not continue processing.

In order to avoid image path duplication, the script uses the MD5 value of the image as the file name of the upload. In addition, it is convenient to replace the version of the image, and the old material will not be overwritten or lost.

After processing, it should formally enter the upload process. You can see that imgList will push a Uploader method to a Promises array at the end of each processing. This Uploader is our main upload process.

④ Tencent cloud initialization

const COS = require('cos-nodejs-sdk-v5')
const cos = new COS({ SecretId, SecretKey });
Copy the code

Before we can talk about upload operation, we need to initialize Tencent Cloud SDK. The whole initialization is very simple, just input the SecretId and SecretKey we see in the console to complete the initialization.

⑤ Upload pictures

Once the SDK is initialized, images can be uploaded to the specified bucket via cos’s putObject method. Since images are uploaded in batches, you need to modify the SDK’s putObject method to make it promise mode in preparation for subsequent promise.all calls.

/ /...
function uploader(fileName, fileBuffer, oldSrc) {
    return new Promise((resolve) = > {
        cos.putObject({
            Bucket,
            Region,
            Key: fileName,
            StorageClass: 'STANDARD'.Body: fileBuffer,
        }, function(err, data) {
            if(! err && data) { resolve({ oldSrc,newSrc: `https://${data.Location}` })
            }
            resolve(null)}); })}Copy the code

For convenience, the exception branch will not be handled. If an error occurs, resolve will be handled, but it can be checked in subsequent callback events, which will not be covered in this article.

After the upload is successful, Tencent Cloud will return the access path data.Location of the file, but this path does not have protocol, so I manually add protocol to it when output, and attach the path of the picture in Markdown to prepare for the next replacement.

Promise.all(promises).then((res) = > {
    let newContent = content
    res.forEach((item) = > {
        if (item && item.newSrc) {
            newContent = newContent.replace(item.oldSrc, item.newSrc)
        }
    })
    const mdName = path.basename(mdPath).split('. ') [0]
    fs.writeFileSync(mdPath.replace(mdName, `${mdName}-The ${Date.now()}`), newContent)
})
Copy the code

After the batch upload is complete, the path will be replaced. Since I output the original path of the image in resolve, I only need to replace oldSrc with newSrc when processing the promise output, and then output the new Markdown file. This can also overwrite the source file, because the script has done the image path screening, so do not worry about the problem of repeated upload.

summary

At this point, we have a little script that automatically detects blog content and uploads images. We just type:

node ./image.js image-script/content.md
Copy the code

The upload process begins, and the Markdown document that replaced the image link is output.

In addition to the manual uploading of pictures, uploading pictures to Tencent Cloud can also facilitate us to publish blogs to the wechat public account, because the direct use of static resources of Tencent cloud in the public account will not be blocked, we do not need to re-upload pictures and proofreading in the editing interface of the public account.