前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MarkDown写作上传图床神器picGo原理

MarkDown写作上传图床神器picGo原理

原创
作者头像
brzhang
修改2020-07-06 17:00:17
2.2K0
修改2020-07-06 17:00:17
举报
文章被收录于专栏:玩转全栈玩转全栈

背景

最近朋友推荐了一个用markdown写博客的神器,typora,用了一下,真的是好用到哭,之前是使用的markText,其实也挺好用的,这两个都是开源的,相对来说,Marktext做的更加美观点,Marktext唯一的缺点是,我在使用的时候,粘贴图片时上传是有bug的。因此,决定用下typora。切换到typora之后,发现快捷建基本上和Marktext是一样的,真的是无痛切入,然后剩下的就是配置一下如何上传图片了。

配置图片上传

配置图片地方可以通过快捷键cmd+,呼出。上传服务有多张方式,我选择Custom Command,执行的命名是PicGo.

首先我们来探讨一下,为何使用PicGo。其实,typora是有非常多种选择的:

对比了一下iPicuPic,他俩都是收费的,因此在有免费的情况下优先不考虑,然后picGo是有GUI版本和命令行(CLI)版本的,这里我之所以考虑CLI版本的,是因为,实际上,上传图片只有在我写markdown的时候需要,因此,装一个GUI版本的显得有些重,在者,安装包也有点大,Mac空间还是有限的,本着能省就省的原则,果断选择安装CLI版本。

这里注意一下,在配置命令的时候:

1、如果说找不到node,那么你需要认为指定node路径。因为通过源码可以发现,picgo默认的node路径是:#!/usr/bin/env node

<img src="https://raw.githubusercontent.com/bravekingzhang/pic_go/master/2020/0E505D78-BE29-41A6-8459-A5A2D99C0DAD.png" alt="0E505D78-BE29-41A6-8459-A5A2D99C0DAD" style="zoom: 50%;" />

2、如果你公司的网络有代理,那么请使用 --proxy|-p 添加代理,比如: -p http://127.0.0.1:12379

PicGo实现原理

如图所示,复制一张图片,在命令行下执行上述命令,图片就上传了,我这里配置的是github作为图床。用了之后,真心感慨,PicGo是在是好用,对于我现在写markdown的需求来说,当真是无可挑剔了。所以,想着,是否应该研究下他的原理呢?

picgo-core
picgo-core

通过这个流程图,我们可以发现,picgo V2版本确实比较清晰,让我想起了移动端那个图片加载神器Glide,可以提供各种各样的Transformer,来做图片处理,比如圆角,加边框,缩放,加水印等等。尽然没想到,一个小小的PicGo居然也做得这么人性化。

先看看,picgo的主流程,如何实现图片上传的。

首先,我们知道,PicGo上传有两种方式:

1、直接上传剪切板复制的图片。

2、上传指定路径下的图片。

其实,两种方式都是一样的,通过源码可以看到,通过剪切板方式传图片,实际上也是最终转化为图片路径的方式。

所以,我们的主流程还是没变,那就是 upload imagePath.其实就是上面那个 lifecycle.start(input)了,所以,跟踪一下:

代码语言:txt
复制
async start (input: any[]): Promise<PicGo> {
    try {
      // images input
      if (!Array.isArray(input)) {
        throw new Error('Input must be an array.')
      }
      this.ctx.input = input
      this.ctx.output = []

      // lifecycle main
      await this.beforeTransform()
      await this.doTransform()
      await this.beforeUpload()
      await this.doUpload()
      await this.afterUpload()
      return this.ctx
    } catch (e) {
      this.ctx.log.warn('failed')
      this.ctx.emit('uploadProgress', -1)
      this.ctx.emit('failed', e)
      this.ctx.log.error(e)
      if (this.ctx.getConfig<Undefinable<string>>('debug')) {
        throw e
      }
      return this.ctx
    }
  }

可以看到,这里就是我们原理环节提到的那个流程了,doTransform->doUpload等环节了。我们先看看Transform做了些什么?

代码语言:txt
复制
private async doTransform (): Promise<PicGo> {
    this.ctx.emit('uploadProgress', 30)
    this.ctx.log.info('Transforming...')
    const type = this.ctx.getConfig<Undefinable<string>>('picBed.transformer') || 'path'
    let transformer = this.ctx.helper.transformer.get(type)
    if (!transformer) {
      transformer = this.ctx.helper.transformer.get('path')
      this.ctx.log.warn(`Can't find transformer - ${type}, switch to default transformer - path`)
    }
    await transformer?.handle(this.ctx)
    return this.ctx
  }

默认就是找到path这个来处理,而这个path,我们看看,他在plugins/transformer/包下,看下实际上做了啥?

代码语言:txt
复制
const handle = async (ctx: PicGo): Promise<PicGo> => {
  const results: IImgInfo[] = ctx.output
  await Promise.all(ctx.input.map(async (item: string, index: number) => {
    let info: IPathTransformedImgInfo
    if (isUrl(item)) {
      info = await getURLFile(item)
    } else {
      info = await getFSFile(item)
    }
    if (info.success && info.buffer) {
      try {
        const imgSize = getImgSize(ctx, info.buffer, item)
        results[index] = {
          buffer: info.buffer,
          fileName: info.fileName,
          width: imgSize.width,
          height: imgSize.height,
          extname: info.extname
        }
      } catch (e) {
        ctx.log.error(e)
      }
    } else {
      ctx.log.error(info.reason)
    }
  }))
  // remove empty item
  ctx.output = results.filter(item => item)
  return ctx
}

其实就是处理下拿到 IPathTransformedImgInfo 这个信息,这个信息其实是有冗余的,因为PicGo实现的是多图床上传,因此,不同的图片所需的参数必然不同,需要一个可以包容较多场景的描述信息,而这个就是IPathTransformedImgInfo

代码语言:txt
复制
export interface IImgInfo {
  buffer?: Buffer
  base64Image?: string
  fileName?: string
  width?: number
  height?: number
  extname?: string
  [propName: string]: any
}

export interface IPathTransformedImgInfo extends IImgInfo {
  success: boolean
}

接下来,我们直接进入upload环节,其代码放在/plugin/upload中,这里面,作者已经预制了很多个图床上传插件了,因为我使用的github图床,因此就看了下github:

代码语言:txt
复制
const handle = async (ctx: PicGo): Promise<PicGo> => {
  const githubOptions = ctx.getConfig<IGithubConfig>('picBed.github')
  if (!githubOptions) {
    throw new Error('Can\'t find github config')
  }
  try {
    const imgList = ctx.output
    for (const img of imgList) {
      if (img.fileName && img.buffer) {
        const base64Image = img.base64Image || Buffer.from(img.buffer).toString('base64')
        const data = {
          message: 'Upload by PicGo',
          branch: githubOptions.branch,
          content: base64Image,
          path: githubOptions.path + encodeURI(img.fileName)
        }
        const postConfig = postOptions(img.fileName, githubOptions, data)
        const body = await ctx.Request.request(postConfig)
        if (body) {
          delete img.base64Image
          delete img.buffer
          if (githubOptions.customUrl) {
            img.imgUrl = `${githubOptions.customUrl}/${githubOptions.path}${img.fileName}`
          } else {
            img.imgUrl = body.content.download_url
          }
        } else {
          throw new Error('Server error, please try again')
        }
      }
    }
    return ctx
  } catch (err) {
    ctx.emit('notification', {
      title: '上传失败',
      body: '服务端出错,请重试'
    })
    throw err
  }
}

通过这个重配置文件中读取我们对于github图床的配置,比如分支,token等信息,对,就是这行代码。

const githubOptions = ctx.getConfig<IGithubConfig>('picBed.github')

其他部分就是很简单了,对接github的文件上传api,我们关心拿到结果之后,拿到结果之后,如果用户配置了customUrl,那么这里就可以把图片链接包装为CDN链接,因为考虑到有些地方访问github图片还是挺慢的,这个是在是太人性化了。

总结

单纯的实现文件复制到剪切板,然后调用github的接口上传到github上,这个过程其实不难,关键是如何做的这么通用,足见作者是花了心思的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 配置图片上传
    • 这里注意一下,在配置命令的时候:
    • PicGo实现原理
      • 先看看,picgo的主流程,如何实现图片上传的。
      • 总结
      相关产品与服务
      内容分发网络 CDN
      内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档