配置文件
- 购买oss 服务
- 购买cdn 服务
- 添加Bucket 列表
- 添加对象存储 OSS配置 域名配置
oss.console.aliyun.com/bucket/oss-…
上传配置代码
../config/index。
// 阿里云 OSS 配置,Sam 老师提供
aliyunOSSConf: {
accessKeyId: 'LTAI5tFxxxxHKxxmCx',
accessKeySecret: '8vwEOxxxxbmzeh7Sdtv8Kvum',
bucket: 'onespot-lego',
region: 'oss-cn-beijing',
},
// 阿里云 OSS CDN 配置,Sam 老师提供
aliyunOSS_CDNHost: 'xxxxx.website',
上传代码逻辑
/**
* @description 上传文件到 oss
*/
const fs = require('fs')
const url = require('url')
const OSS = require('ali-oss')
const { aliyunOSSConf, aliyunOSS_CDNHost: CDNHost } = require('../config/index')
const { isPrd } = require('../utils/env')
// 初始化 oss 实例
const client = new OSS(aliyunOSSConf)
/**
* 替换 url 的 host 为 CDN host
* @param {string} u url
*/
function replaceCDNHost(u = '') {
if (!u) return u
const res = url.parse(u)
let { protocol } = res
if (isPrd) protocol = 'https:' // 线上环境,强行 https
const { path } = res
const u1 = `${protocol}//${CDNHost}${path}` // 替换 CDN host
return u1
}
/**
* 上传文件到 oss
* @param {string} fileName 文件名
* @param {string} filePath 文件路径
*/
async function uploadOSS(fileName, filePath) {
const stream = fs.createReadStream(filePath)
try {
const folder = 'upload-files' // OSS 的文件夹
// 使用 stream 上传,效率高
const res = await client.putStream(`${folder}/${fileName}`, stream)
return replaceCDNHost(res.url)
} catch (ex) {
console.error('阿里云 OSS 上传错误', ex)
// TODO 报警
throw new Error('阿里云 OSS 上传错误')
}
}
module.exports = uploadOSS
主要上传代码是这里
const folder = 'upload-files' // OSS 的文件夹
const res = await client.putStream(`${folder}/${fileName}`, stream)
return replaceCDNHost(res.url)
上传功能开发
/**
* @description 上传图片
*/
const fs = require('fs')
const path = require('path')
const _ = require('lodash')
const formidable = require('formidable')
const { isWindows } = require('../../utils/util')
const { uploadImgFailInfo } = require('../../res-model/failInfo/index')
const { ErrorRes, SuccessRes } = require('../../res-model/index')
const uploadOSS = require('../../vendor/uploadOSS')
// windows 系统,临时存储文件的目录
const TMP_PATH_WINDOWS = 'tmp-files-windows'
const form = formidable({ multiples: true })
// windows 系统,处理 rename 报错
if (isWindows) {
const tmpPath = path.resolve(__dirname, '..', '..', '..', TMP_PATH_WINDOWS)
if (!fs.existsSync(tmpPath)) {
fs.mkdirSync(tmpPath)
}
form.uploadDir = TMP_PATH_WINDOWS
}
/**
* 给 fileName 加个后缀,防止重复。如 `a.png` 变为 `a-xxx.png`
*/
function addSuffixForFileName(fileName = '') {
// 用随机数,做一个后缀
const suffix = Math.random().toString().slice(-6)
if (!fileName) return ''
const lastPointIndex = fileName.lastIndexOf('.')
if (lastPointIndex < 0) {
// 文件名没有后缀名
return `${fileName}-${suffix}`
}
// 文件名有后缀名
return `${fileName.slice(0, lastPointIndex)}-${suffix}${fileName.slice(lastPointIndex)}`
}
/**
* 通过 formidable 上传图片
* @param {object} req ctx.req
*/
function uploadImgByFormidable(req) {
const p = new Promise((resolve, reject) => {
form.parse(req, async function upload(err, fields, files) {
if (err) {
reject(err)
}
// console.log('fields.....', fields) // formData 其他参数,格式如如 { bbb: '123', ccc: 'abc' }
// 遍历所有图片,并上传
const filesKeys = Object.keys(files)
try {
const links = await Promise.all(
filesKeys.map(name => {
const file = files[name]
let fileName = file.name || name
fileName = addSuffixForFileName(fileName) // 给 name 加个后缀,防止名称重复
return uploadOSS(fileName, file.path)
})
)
// 删除源文件
_.forEach(files, file => {
fs.unlinkSync(file.path)
})
// 返回结果
resolve(links)
} catch (ex) {
reject(ex)
}
})
})
return p
}
/**
* 上传图片
* @param {object} req ctx.req
*/
async function uploadImg(req) {
let urls
try {
urls = await uploadImgByFormidable(req)
} catch (ex) {
console.error('上传图片错误', ex)
return new ErrorRes(uploadImgFailInfo)
}
return new SuccessRes({
urls,
})
}
module.exports = uploadImg