Github图床使用

图床就是公网上存放图片的地方,常用的有七牛,又拍云,新浪微博等,前面两个图床是免费的,但是需要绑定到自己已备案的域名,自己没有域名,就算了,新浪微博直接用就行了,但是据说有可能会被删除。用github的图床就没有上面这两个问题,而且查看和管理起来也很方便,所以就在想怎么实现。一顿搜索之后在网上找到了一个APP,PicGo,它里面有一个上传图片到GitHub图床的功能。

Github图床使用_第1张图片
PicGo

PicGo在使用GitHub图床之前要先配置,仓库名一定要加上你的username,我刚开始填的是repository的名字,就一直上传不成功,分支名一般就用master,Token需要到设置中添加一个,路径是这个,点击Generate a new token,填写一个Note,把第一组Scope勾上,生成就可以了,注意这个token只能在生成的时候可以查看,关掉页面就看不到了,需要重新生成了。

Github图床使用_第2张图片
New token

Github图床使用_第3张图片
GitHub设置

把token填进去,点击确定,去测试一下,没有问题。

这是一个开源的APP,可以去项目中找找GitHub图床上传的代码,于是直接把代码clone下来。项目是用electron-vue写的,之前虽然没写过,但是HTML/JS还是看得懂一点的。就一点一点地扒代码。

首先全文搜索了一下github,一通找了之后发现GitHub.vue这个页面的布局跟上面的设置页面很像。找到了一个确定和设为默认图床两个按钮,确定按钮调用了confirm方法,首先是form校验,通过后把form数据保存起来。

    confirm () {
      this.$refs.github.validate((valid) => {
        if (valid) {
          this.$db.set('picBed.github', this.form).write()
          const successNotification = new window.Notification('设置结果', {
            body: '设置成功'
          })
          successNotification.onclick = () => {
            return true
          }
        } else {
          return false
        }
      })
    }

设为默认图床,这个按钮点击后调用setDefaultPicBed这个方法

    // ConfirmButtonMixin.js
    setDefaultPicBed (type) {
      this.$db.read().set('picBed.current', type).write()
      this.defaultPicBed = type
      const successNotification = new window.Notification('设置默认图床', {
        body: '设置成功'
      })
      successNotification.onclick = () => {
        return true
      }
    }

到这里GitHub设置相关的已经完成了,那我要找一下上面的设置在哪里有用到。我就又全文搜索了一下上面的keypicBed.current,在一个Upload.vue文件中找到了获取了这个key对应的值,经过比对,发现这就是上面第一个节目对接的文件。

然后分析里面的布局,有一个引起了我的注意:

将文件拖到此处,或 点击上传

这是中心区域的布局,可以看到有一个file-uploader,触发的事件是onChange方法:

    onChange (e) {
      this.ipcSendFiles(e.target.files)
      document.getElementById('file-uploader').value = ''
    },
    ipcSendFiles (files) {
      let sendFiles = []
      Array.from(files).forEach((item, index) => {
        let obj = {
          name: item.name,
          path: item.path
        }
        sendFiles.push(obj)
      })
      this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
    },

说实话到这里就开始闷逼了,ipcSendFiles方法里最后一句是什么意思,完全不明白。然后就开始猜测,uploadChoosedFiles是不是一个方法,sendFiles是参数,这个就有点像OC语法中的objc_msgSend,全文搜索uploadChoosedFiles,果然在index.js中找到了这个方法的定义:

const uploadChoosedFiles = async (webContents, files) => {
  const input = files.map(item => item.path)
  const imgs = await new Uploader(input, webContents).upload()
  // 省略后面代码...
}

这里最关键的就是构建了一个Uploader对象,并调用upload方法,里面创建了一个PicGo对象,调用upload方法,再追踪这个PicGo类就找不到了,只找到下面这个import:

const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo')

刚开始真的不知道该怎么继续了,因为对JS语法的理解也就只是在写写业务的层面,基本没有接触过webpack,去接了杯水,回来后想这是不是就是一个普通的依赖呢,代码里没有一个picgo.js的文件,就到package.json中找(这是一个前端项目管理依赖的文件,类似已iOS中的Podfile),果然在dependencies中有一个"picgo": "^1.3.7"。带NPM官网搜索picgo这个包,找到了下面这个

Github图床使用_第4张图片
picgo @ npmjs.com

右边有这个包的官网和github代码仓库,于是跳转过去,又把代码clone下来。拿到代码后在src目录下面翻找,看到有一个PicGo.ts的文件(TypeScript),应该就是这个了。

里面有一个upload方法

async upload (input?: any[]): Promise {
    if (this.configPath === '') return this.log.error('The configuration file only supports JSON format.')
    // upload from clipboard
    if (input === undefined || input.length === 0) {
      try {
        const { imgPath, isExistFile } = await getClipboardImage(this)
        if (imgPath === 'no image') {
          throw new Error('image not found in clipboard')
        } else {
          this.once('failed', async () => {
            if (!isExistFile) {
              await fs.remove(imgPath)
            }
          })
          this.once('finished', async () => {
            if (!isExistFile) {
              await fs.remove(imgPath)
            }
          })
          await this.lifecycle.start([imgPath])
        }
      } catch (e) {
        this.log.error(e)
        this.emit('failed', e)
        throw e
      }
    } else {
      // upload from path
      await this.lifecycle.start(input)
    }
  }

看到最终调用的是lifecycle.start()方法,继续追进去,lifecycle.start() -> lifecycle.doUpload()

  private async doUpload (ctx: PicGo): Promise {
    this.ctx.log.info('Uploading...')
    let type = ctx.config.picBed.uploader || ctx.config.picBed.current || 'smms'
    let uploader = this.ctx.helper.uploader.get(type)
    if (!uploader) {
      type = 'smms'
      uploader = this.ctx.helper.uploader.get('smms')
      ctx.log.warn(`Can't find uploader - ${type}, swtich to default uploader - smms`)
    }
    await uploader.handle(ctx)
    for (let i in ctx.output) {
      ctx.output[i].type = type
    }
    return ctx
  }

里面有个根据type获取uploader的方法,这个type使用的是picBed.current,这个是不是很熟悉,就是上面设置默认图床设置的key。分析获取uploader对象的方法,ctx是PicGo对象,helper以及uploader是在PicGo对象的构造方法中初始化的,构造函数中uploader还是一个空的plugin,在init方法中调用了uploaders方法,这个方法正真给uploader对象添加了多个不同类型的Uploader具体实现。这也是策略模式的一种实践。

export default (ctx: PicGo): void => {
  ctx.helper.uploader.register('smms', SMMSUploader)
  ctx.helper.uploader.register('tcyun', tcYunUploader)
  ctx.helper.uploader.register('weibo', weiboUploader)
  ctx.helper.uploader.register('github', githubUploader)
  ctx.helper.uploader.register('qiniu', qiniuUploader)
  ctx.helper.uploader.register('imgur', imgurUploader)
  ctx.helper.uploader.register('aliyun', aliYunUploader)
  ctx.helper.uploader.register('upyun', upYunUploader)
}

到这里,我们已经找到了github图床上传的具体实现类,除此之外,还有很多其他类型的,比例微博,七牛,又拍云等,这次我们只关注github的实现。进入github.ts这个文件,查看handle方法。

const postOptions = (fileName: string, options: any, data: any): any => {
  const path = options.path || ''
  const { token, repo } = options
  return {
    method: 'PUT',
    url: `https://api.github.com/repos/${repo}/contents/${encodeURI(path)}${encodeURI(fileName)}`,
    headers: {
      Authorization: `token ${token}`,
      'User-Agent': 'PicGo'
    },
    body: data,
    json: true
  }
}

// handle方法片段
let base64Image = imgList[i].base64Image || Buffer.from(imgList[i].buffer).toString('base64')
const data = {
  message: 'Upload by PicGo',
  branch: githubOptions.branch,
  content: base64Image,
  path: githubOptions.path + encodeURI(imgList[i].fileName)
}
const postConfig = postOptions(imgList[i].fileName, githubOptions, data)
const body = await ctx.Request.request(postConfig)

对图片进行base64,转成字符串,构建请求头和请求参数,PUT方法发送请求。

到这里已经很兴奋了,这其实就是一个Http请求就可以把图片放到github上去了,赶紧用第三方的HTTP客户端试一下自己拼一个请求。我这里使用Paw这个软件。

Github图床使用_第5张图片
Header
Body

这里User-Agent使用了默认的,文件名使用了Paw中的Timestamp方法,content使用文件的base64,成功了,到github仓库中也看到了上传的这张图片。好厉害。

开发者网站

后来去github的developer页面,才发现自己走了一大段弯路,在Github开发者网站有专门一个章节介绍怎么组装一个创建文件的请求,以后想要实现一个功能,要先找一下官方有没有相关的文档。

Github图床使用_第6张图片
GitHub开发者网站

其实,不光是图片,任何的文件都可以通过这种方式上传到github上。

这篇文章也是记录一下自己通过这个开源项目寻找关键代码的过程,可能会有点啰嗦,但是这个过程对我来说很奇妙。通过这个工程也可以看其他几个图床的上传方式。

你可能感兴趣的:(Github图床使用)