aws-sdk for JavaScript 對接私有云對象存儲

背景

本來經過 NFS 的方式實現了和私有云(騰訊)的對接,可是 NFS 的方式上傳文件會有延時,因此不得不經過其餘方式上傳文件。經過集成 aws-sdk,用類的方式封裝了 s3 實例的建立過程和上傳邏輯,提供更加簡便的上傳入口。javascript

實現

封裝上傳邏輯

這裏只是簡單使用了上傳的功能,並且是對接的私有云,更多關於 sdk 的使用方法能夠參考官方 API 文檔java

引入 sdk 依賴

npm install aws-sdk --save

封裝上傳邏輯

這裏簡單實現了一個類,在構造函數裏建立 s3 實例,options 是配置選項,能夠在 upload() 中傳,我這裏是設置默認值。upload() 中返回 Promise 實例,調用方能夠獲取上傳成功後的數據作其餘操做。git

import AWS from 'aws-sdk'

class CloudStorage {
  constructor () {
    this.s3 = new AWS.S3({
      apiVersion: '2006-03-01',
      endpoint: /** 上傳地址 */ 'xxx',
      accessKeyId: /** 密鑰 */ 'xxx',
      secretAccessKey: /** 私鑰 */ 'xxx',
      s3ForcePathStyle: true
    })
    this.options = { partSize: 10 * 1024 * 1024, queueSize: 1 }
  }

  upload (bucket = 'default', dir = 'default', file) {
    const self = this
    return new Promise((resolve, reject) => {
      self.s3.upload({
        Bucket: bucket,
        Key: `${dir}/${new Date().getTime()}/${file.name}`,
        Body: file.raw
      }, self.options, (err, data) => {
        if (err) {
          console.log('文件上傳雲存儲失敗', err)
          reject(err)
        } else {
          console.log('文件上傳雲存儲成功', data)
          resolve(data)
        }
      })
    })
  }
}

使用

new CloudStorage().upload(bucket, dir, file)

擴展/優化

單例模式

上面的實現有一個問題,就是每次建立 CloudStorage 對象時,都會去調用一次構造函數,也就形成了重複地建立 s3 實例,但其實咱們只要建立一次就好了。所以這裏能夠經過單例模式進行優化,只對外提供惟一的實例。 單例模式代碼以下:github

function getInstance () {
  if (!CloudStorage.instance) {
    CloudStorage.instance = new CloudStorage()
  }
  return CloudStorage.instance
}

// 調用
getInstance().upload(bucket, dir, file)

柯里化

經過上面的單例模式優化後,已經能夠很簡單地調用 upload 上傳文件了。可是別急,還能夠繼續優化。npm

若是咱們像上面同樣調用,上傳單個文件是沒問題,可是若是是這樣的場景:多個文件上傳或者多個文件上傳到不一樣的 bucket 或文件夾下,又好像仍是有些多餘的操做存在,咱們來看一下調用:api

getInstance().upload('bucket1', 'dir1', file1)
getInstance().upload('bucket1', 'dir1', file2)
getInstance().upload('bucket2', 'dir2', file3)
...

能夠這裏的 bucket 和文件夾參數重複地去傳了,有沒有什麼辦法能夠減小這樣的參數傳遞?有!經過柯里化,就是一種將一個多參數函數轉換成一系列使用一個參數的函數的技術。函數

能夠經過判斷參數的數量來判斷,當參數知足函數須要以後才真正調用真實的函數。代碼實現:優化

/**
 * 這裏的實現不止可接收一個參數
 * @param {function} 真實調用的函數
 * @return {function} 轉換後的函數
 */
function curry (fn) {
  const judge = (...args) =>
    args.length < fn.length
      ? (...nextArgs) => judge(...args, ...nextArgs)
      : fn(...args)
  return judge
}

// 如今的調用方式
const curryUpload = curry(getInstance().upload)
let bucket1Upload = curryUpload('bucket1')
let bucket2Upload = curryUpload('bucket2')
let dir1Upload = bucket1Upload('dir1')
let dir2Upload = bucket2Upload('dir2')
dir1Upload(file1)
dir2Upload(file2)
dir1Upload(file3)

是否是感受這樣調用舒服多了:)。 可是由於使用了柯里化,這裏要注意有兩個坑的地方:this

  • 函數參數有默認值的狀況下,函數的 length 值爲 0,因此不能在參數列表的地方設置默認值,能夠在函數體裏判斷。
  • 用了柯里化以後(由於個人柯里化實現是經過箭頭函數的),返回的函數 this 執行不是指向 CloudStorage 實例了,但咱們仍是能夠經過 getInstance() 來獲取實例。

完整版代碼以下:code

import AWS from 'aws-sdk'
import { curry } from '../utils/utils' // 柯里化的函數,你們也可用本身的實現

class CloudStorage {
  constructor () {
    this.s3 = new AWS.S3({
      apiVersion: '2006-03-01',
      endpoint: /** 上傳地址 */ 'xxx',
      accessKeyId: /** 密鑰 */ 'xxx',
      secretAccessKey: /** 私鑰 */ 'xxx',
      s3ForcePathStyle: true
    })
    this.options = { partSize: 10 * 1024 * 1024, queueSize: 1 }
  }

  upload (bucket, dir, file) {
    if (!bucket) bucket = 'default'
    if (!dir) dir = 'default'
    const instance = getInstance()
    return new Promise((resolve, reject) => {
      instance.s3.upload({
        Bucket: bucket,
        Key: `${dir}/${new Date().getTime()}/${file.name}`,
        Body: file.raw
      }, instance.options, (err, data) => {
        if (err) {
          console.log('文件上傳雲存儲失敗', err)
          reject(err)
        } else {
          console.log('文件上傳雲存儲成功', data)
          resolve(data)
        }
      })
    })
  }
}

function getInstance () {
  if (!CloudStorage.instance) {
    CloudStorage.instance = new CloudStorage()
  }
  return CloudStorage.instance
}

export const upload = curry(getInstance().upload)
相關文章
相關標籤/搜索