Develop a PicGo image compression plugin

Open source address

github: https://github.com/liujinpen/picgo-plugin-compress-tinypng.git

npm: https://www.npmjs.com/package/picgo-plugin-tinypng

Foreword

When writing a personal blog, you need to upload pictures to the picture bed. PicGo is a pretty good tool, but when you find that the pictures are large in size during use, it will waste cloud storage space because you don’t need such a high resolution. Before, I chose to manually compress larger image files, which was very troublesome.

Fortunately, PicGo itself supports plug-ins. After searching, I found that there is already a plug-in that supports compression: picgo-plugin-compress, but PicGo-Core starts from v1.5.0, and the bottom layer of the request package starts from Request-Promise -Native has been transferred to axios, so this plugin will report an error when used after PicGo client version 2.3.0, and the author has not updated it for a long time.

After learning about tinify, I decided to develop a picture compression plug-in adapted to PicGo.

Compression effect

tinify official: The impact of compressed pictures on vision is almost invisible, but there is a very big difference in file size.

There are few test samples here, but the effect can already be felt:

  • Original image: 1.78MB -> Compressed: 511.9KB

  • Original image: 27.4KB -> Compressed: 11.5KB

tinify-API usage

tinify provides developers with a variety of API usage methods. You can register your email address to obtain an API key. An API key is available for 500 free times per month. For specific API usage, please refer to https://tinify.cn/developers.

PicGo plug-in development

The development process is described in more detail in the official plug-in development document, which requires a certain js foundation.

1. Use plugin template

To use the plugin template, you first need to install picgo globally

npm install picgo -g

Initialize the project

picgo init plugin tinypng
Note: tinypng is the project name

The creation process can customize options as needed, from top to bottom:

  • plugin name
  • plugin description
  • author
  • The component corresponding to the developed plug-in (see the document for details), what needs to be done to compress the image is to convert the image into a buffer and provide it to picgo, so here select the transformer component
  • Whether CLI plug-in (select No if it needs to be used in the graphical client)
  • Use TypeScript or JavaScript (the official recommendation is that TS can have better syntax prompts. In fact, if you develop in WebStorm, the experience of using JS is also very good)
  • Whether the plugin needs to configure shortcut keys

After the project is created, you can use the IDE for development.

2. Plug-in configuration items

Using tinify’s API requires a key, so configure the entry in the plugin:

const config = ctx => {<!-- -->
  let config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
  if (!config) {<!-- -->
    config = {<!-- -->}
  }
  ctx.log.info('read config:', config)
  return [{<!-- -->
    name: 'key', // configuration name
    type: 'input', // configuration type, input is displayed as an input box
    default: config.key || null, // default value
    required: true, // is required
    message: 'Fill in the API Key of tinypng' // placeholder
  }]
}

3. Compressed file

When we drag a local image into PicGo, what we get is the path of the local file.

Compressed logic:

  1. Use the readFile method of the fs-extra library to convert the image into a binary stream;
  2. Construct the request required by tinify (formatted API key, file buffer, other parameters, etc.)
  3. Use PicGo’s request library to send requests
  4. Get the compressed image address returned by tinify
  5. Use PicGo’s request library to download the picture (the file buffer is obtained)
  6. Construct the output format required by the transformer component

So far, most of the work of the plug-in has been completed. After returning the result of our construction, PicGo can upload the compressed file to the image bed.

/**
 * Read the local image, upload it to tinypng for compression, get the compressed url, and download the image buffer
 *
 * @param ctx PicGo
 * @param imageUrl local image path
 * @param key api key in configuration
 * @returns {Promise<Buffer>}
 */
async function tinypngKeyCompress(ctx, imageUrl, key) {<!-- -->
  return await fs.readFile(imageUrl).then(fileData => {<!-- -->
    // 1. Refer to https://tinify.cn/developers/reference for the format of the construction request parameters (request to tinypng)
    const bearer = Base64. stringify(Utf8. parse(`api:${<!-- -->key}`))
    const fetchOptions = {<!-- -->
      method: 'POST',
      url: 'https://api.tinify.com/shrink',
      json: true,
      resolveWithFullResponse: true,
      headers: {<!-- -->
        Host: 'api.tinify.com',
        Authorization: `Basic ${<!-- -->bearer}`
      },
      data: fileData
    }

    // 2. Send compression request
    return ctx.request(fetchOptions).then(response => {<!-- -->
      // 3. Get the url of the compressed image
      if (response.status & amp; & amp; response.status >= 200 & amp; & amp; response.status <= 299) {<!-- -->
        const location = response. headers. location
        ctx.log.info('compressed address:', location)
        
        // 4. Get the network image from the url
        return utils.fetchImage(ctx, location).then(res => {<!-- -->
          // 5. Obtain the compressed image buffer and construct the format required by picGO
          return utils.getImageInfo(imageUrl, res.data)
        })
      }
      throw new Error('compression error')
    })
  })
}

Related Tools

/**
 * Get image information
 * @param imageUrl
 * @param buffer
 * @returns {<!-- -->{fileName: string, width: number, buffer, extname: string, height: number}}
 */
function getImageInfo(imageUrl, buffer) {<!-- -->
  const {<!-- -->width, height} = imageSize. imageSize(buffer)
  return {<!-- -->
    buffer,
    width: width, //width
    height: height, //height
    fileName: path.basename(imageUrl), // file name
    extname: path.extname(imageUrl), //extension name
  }
}

/**
 * Get file information according to url
 * @param imageUrl
 * @returns {<!-- -->{fileName: string, extname: string}}
 */
function getUrlInfo(imageUrl) {<!-- -->
  return {<!-- -->
    fileName: path. basename(imageUrl),
    extname: path.extname(imageUrl)
  }
}

/**
 * Download network pictures
 * @param ctx
 * @param url
 * @returns {*}
 */
function fetchImage(ctx, url) {<!-- -->
  return ctx.request({<!-- -->
    method: 'GET',
    url,
    encoding: null,
    resolveWithFullResponse: true,
    responseType: 'arraybuffer'
  })
}

4. Complete the plug-in process

This part of the code needs to be organized according to the PicGo plug-in development requirements

module.exports = (ctx) => {<!-- -->
  const handle = ctx => {<!-- -->
    // Get the configured key
    const config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
    const key = config['key']

    // input is a list of paths, and the pictures of each path are compressed first
    const tasks = ctx.input.map(imageUrl => {<!-- -->
      ctx.log.info('Image address:' + imageUrl)
      // call compression method
      return tinypng.tinypngKeyCompress(ctx, imageUrl, key).then(res => {<!-- -->
        return res
      })
    })

    return Promise.all(tasks).then((output) => {<!-- -->
      ctx. output = output
      return ctx
    })
  }

  const config = ctx => {<!-- -->
    let config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
    if (!config) {<!-- -->
      config = {<!-- -->}
    }
    ctx.log.info('read config:', config)
    return [{<!-- -->
      name: 'key', // configuration name
      type: 'input', // configuration type, input is displayed as an input box
      default: config.key || null, // default value
      required: true, // is required
      message: 'Fill in the API Key of tinypng' // placeholder
    }]
  }

  const register = () => {<!-- -->
    ctx.helper.transformer.register('tinypng', {<!-- -->handle})
  }
  return {<!-- -->
    transformer: 'tinypng',
    register,
    config
  }
}

Local testing

If the development template uses JS, you can directly go to PicGo main interface->plug-in settings->import icon in the upper right corner->select the source code directory after the development is completed.

Yes, select the source code directory, and PicGo can successfully load the plug-in. After the loading is complete, it needs to be restarted to take effect.

After importing the plug-in, you need to enable the transformer-tinypng option (disabled means the enable is successful):

Configure the plugin -picgo-plugin-tinypng option to fill in the requested API key and picture bed configuration name (custom):

NPM publish

The plug-in of this article has been released, just search for tinypng in the plug-in settings of PicGo.

For npm publishing, please refer to How to publish your own npm package.

In this way, others can search for it in PicGo’s plug-in settings.

Reference

Develop a PicGo plugin

Getting Started with PicGo Plugin Development

PicGo Official Plugin Development Guide

PicGo configures image compression (no longer available)

picgo-plugin-compress (plugin has been stopped)