uploadimage-to-qiniu

VS Code编辑器右键挑选图片上传到七牛云,主动把链接粘贴到当前光标方位,鼠标悬浮在链接上面支持预览图片

啥也不说先看作用!!!

开发一款超级实用的VS Code插件

第一步:初始化项目

能够根据官网教程初始化项目,首要重视下图几个文件

开发一款超级实用的VS Code插件

第二步:功用开发

  • 先看extension.ts 是插件的进口文件,util目录下面几个文件都是基于extension.ts详细功用的办法完成
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { upImageToQiniu } from './util/upload';
import { getHoverHttpLink, translateImageUrlToBase64 } from './util/handleHover';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
  // Use the console to output diagnostic information (console.log) and errors (console.error)
  // This line of code will only be executed once when your extension is activated
  // The command has been defined in the package.json file
  // Now provide the implementation of the command with registerCommand
  // The commandId parameter must match the command field in package.json
  let texteditor = vscode.commands.registerTextEditorCommand(
    'extension.choosedImage',
    async (textEditor, edit, args) => {
      console.log('挑选图片');
      const qiniuConfig = vscode.workspace.getConfiguration('upload_qiniu_config');
      const uri = await vscode.window.showOpenDialog({
        canSelectFolders: false,
        canSelectMany: false,
        filters: {
          images: ['png', 'jpg', 'gif', 'jpeg', 'svg'],
        },
      });
      if (!uri) {
        return;
      }
      const upConfig = {
        accessKey: qiniuConfig.accessKey,
        secretKey: qiniuConfig.secretKey,
        domain: qiniuConfig.domain,
        gzip: qiniuConfig.gzip,
        scope: qiniuConfig.scope,
        directory: qiniuConfig.directory,
        imageWidth: qiniuConfig.imageWidth,
        formatWebp: qiniuConfig.formatWebp
      };
      const loaclFile = uri[0].fsPath;
      upImageToQiniu(
        loaclFile,
        (res: string) => {
          let url = res;
          // 将图片链接写入编辑器
          if(upConfig.imageWidth !== ''){
            url = `${url}?imageView2/2/w/${upConfig.imageWidth}`;
          }
          if(upConfig.formatWebp){
            url = `${url}/format/webp`;
          }
          console.log('图片上传成功', url);
          addImageUrlToEditor(url);
        },
        upConfig
      );
    }
  );
  // 鼠标悬浮预览图片
  vscode.languages.registerHoverProvider('*', {
    async provideHover(document, position) {
      try {
        const { character } = position;
        // 当前行的文本内容
        const currentLineText = document.lineAt(position).text;
        // 匹配当前行内
        const httpLink = getHoverHttpLink(currentLineText, character);
        var strToBase64 = await translateImageUrlToBase64(httpLink);
        const markString = strToBase64 ? new vscode.MarkdownString(`![](${strToBase64})`, true) : '';
        return {
          contents: [markString],
        };
      } catch (err) {
        console.log('error', err);
      }
    },
  });
  context.subscriptions.push(texteditor);
}
// 将图片链接写入编辑器
function addImageUrlToEditor(url: string) {
  let editor = vscode.window.activeTextEditor;
  if (!editor) {
    return;
  }
  // 替换内容
  const selection = editor.selection;
  editor.edit((editBuilder) => {
    editBuilder.replace(selection, url);
  });
}
// this method is called when your extension is deactivated
export function deactivate() {}
  • util/upload.ts 完成上传到七牛云的办法
const path = require('path');
const fs = require('fs');
const qiniu = require('qiniu');
const imagemin = require('imagemin');
const imageminPngquant = require('imagemin-pngquant');
const imageminJpegtran = require('imagemin-jpegtran');
import * as vscode from 'vscode';
import { getBufferFromFile, bufferToStream } from './base';
// 获取七牛token
const getToken = (accessKey: string, secretKey: string, scope: string) => {
  const options = {
    scope,
  };
  const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
  const putPolicy = new qiniu.rs.PutPolicy(options);
  const uploadToken = putPolicy.uploadToken(mac);
  return uploadToken;
};
// 图片紧缩
const imageGzip = async (loaclFile: string): Promise<any> => {
  const bufferFile = await getBufferFromFile(loaclFile);
  let res;
  try {
    res = await imagemin.buffer(bufferFile, {
      plugins: [
        imageminJpegtran(),
        imageminPngquant({
          quality: [0.6, 0.8],
        }),
      ],
    });
    console.log('图片紧缩成功', res);
  } catch (err) {
    vscode.window.showInformationMessage('图片紧缩失利');
    res = null;
  }
  return res;
};
// 七牛上传装备
export interface QiNiuUpConfig {
  domain: string // 上传后域名
  accessKey: string // 七牛参数
  secretKey: string // 七牛参数
  scope: string // 七牛上传空间
  gzip: boolean // 是否需求紧缩
  directory: string // 指定目录
  imageWidth: string // 图片展示宽度
  formatWebp: boolean // 是否主动转成webp格局
}
// 上传图片到七牛云
export const upImageToQiniu = async (
  loaclFile: string,
  cb: { (res: any): void; (arg0: any): void },
  upConfig: QiNiuUpConfig
) => {
  // 将图片途径一致为 xx/xxx
  const filePathArr = loaclFile.split(path.sep);
  loaclFile = path.posix.join(...filePathArr);
  const config = new qiniu.conf.Config();
  const formUploader = new qiniu.form_up.FormUploader(config);
  const putExtra = new qiniu.form_up.PutExtra();
  const token = getToken(upConfig.accessKey, upConfig.secretKey, upConfig.scope);
  let gzipImage;
  if (upConfig.gzip) {
    console.log('现已开启紧缩');
    gzipImage = await imageGzip(loaclFile);
  }
  const file = filePathArr.pop();
  const fileName = file?.split('.')[0];
  const fileType = file?.split('.')[1];
  // 文件目录+文件名称
  const keyToOverwrite = `${upConfig.directory}/${fileName}-${new Date().getTime()}.${fileType}`;
  // 上传调用办法
  const uploadFnName = gzipImage ? 'putStream' : 'putFile';
  // 上传内容
  const uploadItem = gzipImage ? bufferToStream(gzipImage) : path.normalize(loaclFile);
  // 七牛上传
  formUploader[uploadFnName](
    token,
    keyToOverwrite,
    uploadItem,
    putExtra,
    function (respErr: any, respBody: any, respInfo: any) {
      if (respErr) {
        throw respErr;
      }
      if (respInfo.statusCode === 200) {
        const url = `${upConfig.domain}/${respBody.key}`;
        cb(url);
      } else {
        vscode.window.showInformationMessage(`上传失利: ${respInfo.statusCode}`);
      }
    }
  );
};
  • util/base.ts
const fs = require('fs');
const duplex = require('stream').Duplex;
// 获取buffer
export const getBufferFromFile = (filePath: string): Promise<Buffer> => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, function (err: any, res: any) {
      if (!err) {
        resolve(res);
      }
    });
  });
};
// buffer 转 stream
export const bufferToStream = (buffer: Buffer) => {
  let stream = new duplex();
  stream.push(buffer);
  stream.push(null);
  return stream;
};
  • util/handleHover.ts 完成链接主动粘贴到光标方位和鼠标悬浮链接上面预览图片
import * as https from 'https';
import * as http from 'http';
// 将链接左右两边的引号删掉
const filterHttpLink = (link: string): string => {
  if (link) {
    link = link.substr(0, link.length - 1);
    link = link.substr(1);
  }
  return link;
};
// 获取http链接 在字符串中的方位
const getHttpLinkPosition = (content: string): Array<any> => {
  const regx = /["|'][http(s)://](.*?)["|']/g;
  // @ts-ignore
  const matchArr = [...content.matchAll(regx)];
  const arr: any[] = [];
  matchArr.forEach((item: any) => {
    const url = filterHttpLink(item[0]);
    arr.push({
      start: item.index - 1,
      end: item.index - 1 + url.length,
      value: url,
      length: url.length
    });
  });
  return arr;
};
// 获取hover的 http链接
export const getHoverHttpLink = (content: string, position: number): string => {
  let link = '';
  const httpPositions = getHttpLinkPosition(content);
  if (httpPositions.length) {
    httpPositions.forEach((item) => {
      if (item.start <= position && item.end >= position) {
        link = item.value;
      }
    });
  }
  return link;
};
// 图片增加裁剪参数
export const addImageCropParam = (
  url: string,
  width?: number,
  height?: number,
  type?: number
): string => {
  // 假如url中现已带有裁剪参数,先去掉之前的参数
  const [path] = url.split('?imageView2');
  url = path;
  let cropUrl = type ? `?imageView2/${type}` : '?imageView2/2';
  if (!!width) {
    cropUrl += `/w/${width}`;
  }
  if (!!height) {
    cropUrl += `/h/${height}`;
  }
  if (!!width || !!height) {
    url += cropUrl;
  }
  return url;
};
// 将图片链接转为base64
export const translateImageUrlToBase64 = (url: string): Promise<string> => {
  return new Promise((resolve, reject) => {
    let resUrl = '';
    // 链接是否为https
    const isHttps = url.includes('https');
    if (!url) {
      resolve(resUrl);
    } else {
      url = addImageCropParam(url, 100)
      ;(isHttps ? https : http).get(url, {}, function (res: http.IncomingMessage) {
        const contentType = res.headers['content-type'];
        // 恳求为图片
        if (contentType && contentType.includes('image')) {
          var chunks: Array<any> = []; //用于保存网络恳求不断加载传输的缓冲数据
          var size = 0; //保存缓冲数据的总长度
          res.on('data', function (chunk: any) {
            chunks.push(chunk);
            //累加缓冲数据的长度
            size += chunk.length;
          });
          res.on('end', function (err: any) {
            //Buffer.concat将chunks数组中的缓冲数据拼接起来,回来一个新的Buffer目标赋值给data
            var data = Buffer.concat(chunks, size);
            //将Buffer目标转换为字符串并以base64编码格局显示
            const base64Img = data.toString('base64');
            resolve(`data:image/png;base64,${base64Img}`);
          });
        } else {
          resolve(resUrl);
        }
      });
    }
  });
};

第三步:插件装备

package.json 包含插件的装备信息(插件指令、快捷键、菜单均在此装备)

  • menus
    • editor/context 编辑器上下文菜单
      • when 操控菜单何时呈现
      • command 界说菜单被点击后要履行什么操作
      • group 界说菜单分组
  • command 指令装备
  • configuration 插件装备
"contributes": {
   "menus": {
    "editor/context": [
      {
        "when": "editorFocus",
        "command": "extension.choosedImage",
        "group": "navigation"
      }
    ]
  },
  "commands": [
    {
      "command": "extension.choosedImage",
      "title": "挑选图片"
    }
  ],
  "configuration": [
    {
      "title": "上传七牛插件装备项",
      "properties": {
        "upload_qiniu_config.domain": {
          "type": "string",
          "default": "",
          "description": "设置上传域名"
        },
        "upload_qiniu_config.accessKey": {
          "type": "string",
          "default": "",
          "description": "设置七牛上传accessKey"
        },
        "upload_qiniu_config.secretKey": {
          "type": "string",
          "default": "",
          "description": "设置七牛上传secretKey"
        },
        "upload_qiniu_config.scope": {
          "type": "string",
          "default": "",
          "description": "设置七牛上传上传空间"
        },
        "upload_qiniu_config.gzip": {
          "type": "boolean",
          "default": "true",
          "description": "是否启用图片紧缩"
        },
        "upload_qiniu_config.directory": {
          "type": "string",
          "default": "",
          "description": "设置七牛上传指定目录"
        },
        "upload_qiniu_config.imageWidth": {
          "type": "string",
          "default": "",
          "description": "设置图片展示宽度"
        },
        "upload_qiniu_config.formatWebp": {
          "type": "boolean",
          "default": "",
          "description": "是否主动转成webp格局"
        }
      }
    }
  ]
},

第四步:本地调试

  1. 装置vsce打包东西
npm install -g vsce
  1. 履行打包指令
vsce package

开发一款超级实用的VS Code插件

打包完成后会在根目录出产.vsix文件,点击右键挑选“装置扩展VSIX”

  1. 运转调试

默许情况下,工程现已帮咱们装备好了调试相关参数(有兴趣的能够查看.vscode/launch.json文件的写法),切换到vscode运转和调试面板,然后点击Run Extension,默许会打开一个新窗口,然后咱们就能够在新窗口去测验和验证,调试操控台也能够看到console.log的输出成果。

开发一款超级实用的VS Code插件

本地验证没问题的话,那就能够预备发布了~~

第五步:发布

  1. 注册账号login.live.com/

  2. 拜访aka.ms/SignupAzure…,假如你从来没有使用过Azure,那么会看到如下提示:

开发一款超级实用的VS Code插件

点击继续,默许会创立一个以邮箱前缀为名的安排。

  1. 默许进入安排的主页后,点击右上角的Security

开发一款超级实用的VS Code插件

点击创立新的个人拜访令牌,这儿特别要注意Organization要挑选all accessible organizationsScopes要挑选Full access,不然后面发布会失利。

开发一款超级实用的VS Code插件

创立令牌成功后你需求本地记下来,因为网站是不会帮你保存的。

  1. 获得个人拜访令牌后,使用vsce以下指令创立新的发布者:
vsce create-publisher your-publisher-name

创立成功后会默许登录这个账号,接下来你能够直接发布了,当然,假如你是在其它当地创立的,能够试用vsce login your-publisher-name来登录。

  1. 履行发布指令
npm run pubilsh

发布成功后,能够拜访marketplace.visualstudio.com… 查看

在VSCode中也能够查找进行装置

开发一款超级实用的VS Code插件

  1. 增量发布

版本号:major.minor.patch

假如想让发布之后版本号的patch自增,例如:1.0.2 -> 1.0.3,能够履行指令

vsce publish patch

以此类推…

郑重声明: 文章有参阅自 从零开发Vscode上传图片插件,如有侵权,请联系删去!