背景
本来经过 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)