Github圖床使用

圖床就是公網(wǎng)上存放圖片的地方,常用的有七牛,又拍云,新浪微博等,前面兩個(gè)圖床是免費(fèi)的,但是需要綁定到自己已備案的域名,自己沒有域名,就算了,新浪微博直接用就行了,但是據(jù)說有可能會(huì)被刪除。用github的圖床就沒有上面這兩個(gè)問題,而且查看和管理起來也很方便,所以就在想怎么實(shí)現(xiàn)。一頓搜索之后在網(wǎng)上找到了一個(gè)APP,PicGo,它里面有一個(gè)上傳圖片到GitHub圖床的功能。

PicGo

PicGo在使用GitHub圖床之前要先配置,倉庫名一定要加上你的username,我剛開始填的是repository的名字,就一直上傳不成功,分支名一般就用master,Token需要到設(shè)置中添加一個(gè),路徑是這個(gè),點(diǎn)擊Generate a new token,填寫一個(gè)Note,把第一組Scope勾上,生成就可以了,注意這個(gè)token只能在生成的時(shí)候可以查看,關(guān)掉頁面就看不到了,需要重新生成了。

New token

GitHub設(shè)置

把token填進(jìn)去,點(diǎn)擊確定,去測(cè)試一下,沒有問題。

這是一個(gè)開源的APP,可以去項(xiàng)目中找找GitHub圖床上傳的代碼,于是直接把代碼clone下來。項(xiàng)目是用electron-vue寫的,之前雖然沒寫過,但是HTML/JS還是看得懂一點(diǎn)的。就一點(diǎn)一點(diǎn)地扒代碼。

首先全文搜索了一下github,一通找了之后發(fā)現(xiàn)GitHub.vue這個(gè)頁面的布局跟上面的設(shè)置頁面很像。找到了一個(gè)確定和設(shè)為默認(rèn)圖床兩個(gè)按鈕,確定按鈕調(diào)用了confirm方法,首先是form校驗(yàn),通過后把form數(shù)據(jù)保存起來。

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

設(shè)為默認(rèn)圖床,這個(gè)按鈕點(diǎn)擊后調(diào)用setDefaultPicBed這個(gè)方法

    // ConfirmButtonMixin.js
    setDefaultPicBed (type) {
      this.$db.read().set('picBed.current', type).write()
      this.defaultPicBed = type
      const successNotification = new window.Notification('設(shè)置默認(rèn)圖床', {
        body: '設(shè)置成功'
      })
      successNotification.onclick = () => {
        return true
      }
    }

到這里GitHub設(shè)置相關(guān)的已經(jīng)完成了,那我要找一下上面的設(shè)置在哪里有用到。我就又全文搜索了一下上面的keypicBed.current,在一個(gè)Upload.vue文件中找到了獲取了這個(gè)key對(duì)應(yīng)的值,經(jīng)過比對(duì),發(fā)現(xiàn)這就是上面第一個(gè)節(jié)目對(duì)接的文件。

然后分析里面的布局,有一個(gè)引起了我的注意:

<div id="upload-dragger" @click="openUplodWindow">
  <i class="el-icon-upload"></i>
  <div class="upload-dragger__text">
    將文件拖到此處,或 <span>點(diǎn)擊上傳</span>
  </div>
  <input type="file" id="file-uploader" @change="onChange" multiple>
</div>

這是中心區(qū)域的布局,可以看到有一個(gè)file-uploader,觸發(fā)的事件是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)
    },

說實(shí)話到這里就開始悶逼了,ipcSendFiles方法里最后一句是什么意思,完全不明白。然后就開始猜測(cè),uploadChoosedFiles是不是一個(gè)方法,sendFiles是參數(shù),這個(gè)就有點(diǎn)像OC語法中的objc_msgSend,全文搜索uploadChoosedFiles,果然在index.js中找到了這個(gè)方法的定義:

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

這里最關(guān)鍵的就是構(gòu)建了一個(gè)Uploader對(duì)象,并調(diào)用upload方法,里面創(chuàng)建了一個(gè)PicGo對(duì)象,調(diào)用upload方法,再追蹤這個(gè)PicGo類就找不到了,只找到下面這個(gè)import:

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

剛開始真的不知道該怎么繼續(xù)了,因?yàn)閷?duì)JS語法的理解也就只是在寫寫業(yè)務(wù)的層面,基本沒有接觸過webpack,去接了杯水,回來后想這是不是就是一個(gè)普通的依賴呢,代碼里沒有一個(gè)picgo.js的文件,就到package.json中找(這是一個(gè)前端項(xiàng)目管理依賴的文件,類似已iOS中的Podfile),果然在dependencies中有一個(gè)"picgo": "^1.3.7"。帶NPM官網(wǎng)搜索picgo這個(gè)包,找到了下面這個(gè)

picgo @ npmjs.com

右邊有這個(gè)包的官網(wǎng)和github代碼倉庫,于是跳轉(zhuǎn)過去,又把代碼clone下來。拿到代碼后在src目錄下面翻找,看到有一個(gè)PicGo.ts的文件(TypeScript),應(yīng)該就是這個(gè)了。

里面有一個(gè)upload方法

async upload (input?: any[]): Promise<void | string | Error> {
    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)
    }
  }

看到最終調(diào)用的是lifecycle.start()方法,繼續(xù)追進(jìn)去,lifecycle.start() -> lifecycle.doUpload()

  private async doUpload (ctx: PicGo): Promise<PicGo> {
    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
  }

里面有個(gè)根據(jù)type獲取uploader的方法,這個(gè)type使用的是picBed.current,這個(gè)是不是很熟悉,就是上面設(shè)置默認(rèn)圖床設(shè)置的key。分析獲取uploader對(duì)象的方法,ctx是PicGo對(duì)象,helper以及uploader是在PicGo對(duì)象的構(gòu)造方法中初始化的,構(gòu)造函數(shù)中uploader還是一個(gè)空的plugin,在init方法中調(diào)用了uploaders方法,這個(gè)方法正真給uploader對(duì)象添加了多個(gè)不同類型的Uploader具體實(shí)現(xiàn)。這也是策略模式的一種實(shí)踐。

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)
}

到這里,我們已經(jīng)找到了github圖床上傳的具體實(shí)現(xiàn)類,除此之外,還有很多其他類型的,比例微博,七牛,又拍云等,這次我們只關(guān)注github的實(shí)現(xiàn)。進(jìn)入github.ts這個(gè)文件,查看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)

對(duì)圖片進(jìn)行base64,轉(zhuǎn)成字符串,構(gòu)建請(qǐng)求頭和請(qǐng)求參數(shù),PUT方法發(fā)送請(qǐng)求。

到這里已經(jīng)很興奮了,這其實(shí)就是一個(gè)Http請(qǐng)求就可以把圖片放到github上去了,趕緊用第三方的HTTP客戶端試一下自己拼一個(gè)請(qǐng)求。我這里使用Paw這個(gè)軟件。

Header
Body

這里User-Agent使用了默認(rèn)的,文件名使用了Paw中的Timestamp方法,content使用文件的base64,成功了,到github倉庫中也看到了上傳的這張圖片。好厲害。

開發(fā)者網(wǎng)站

后來去github的developer頁面,才發(fā)現(xiàn)自己走了一大段彎路,在Github開發(fā)者網(wǎng)站有專門一個(gè)章節(jié)介紹怎么組裝一個(gè)創(chuàng)建文件的請(qǐng)求,以后想要實(shí)現(xiàn)一個(gè)功能,要先找一下官方有沒有相關(guān)的文檔。

GitHub開發(fā)者網(wǎng)站

其實(shí),不光是圖片,任何的文件都可以通過這種方式上傳到github上。

這篇文章也是記錄一下自己通過這個(gè)開源項(xiàng)目尋找關(guān)鍵代碼的過程,可能會(huì)有點(diǎn)啰嗦,但是這個(gè)過程對(duì)我來說很奇妙。通過這個(gè)工程也可以看其他幾個(gè)圖床的上傳方式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 項(xiàng)目地址:https://github.com/Molunerfinn/PicGo 本人經(jīng)常使用Markdown來...
    GitHub_itgoyo閱讀 16,162評(píng)論 2 8
  • 對(duì)于許多像筆者一樣使用 Markdown 寫作的人來說,在文章中插入圖片是一件非常普遍的事。而就是這樣一件普遍的事...
    神齊閱讀 2,009評(píng)論 2 48
  • 前言 用過幾款上傳圖片到圖床的軟件,但是自己常用的圖床,比如青云對(duì)象存儲(chǔ)基本都沒有支持的。 剛好前幾天發(fā)現(xiàn)了一款可...
    chengww閱讀 3,351評(píng)論 4 0
  • 高考,一個(gè)熟悉又陌生的名詞。說它熟悉是因?yàn)閺挠浭缕鹁吐牬笕藗冊(cè)谡務(wù)摳呖?,說不熟悉是因?yàn)槲覐膩頉]有接觸過它。...
    雪櫻2閱讀 227評(píng)論 0 0
  • 日光淺淺,夕陽漸沉 旅途慢慢被拉長(zhǎng) 疲倦的知了也悄悄收了聲 睡意沉沉 窗外的青枝微彎 是有風(fēng)吹過 帶走幾絲蒸騰的熱...
    木粥粥閱讀 955評(píng)論 0 4

友情鏈接更多精彩內(nèi)容