使用阿里云oss 上传文件

258 阅读1分钟

配置文件

  1. 购买oss 服务
  2. 购买cdn 服务
  3. 添加Bucket 列表

12.jpg

  1. 添加对象存储 OSS配置 域名配置

oss.console.aliyun.com/bucket/oss-…

11.jpg

上传配置代码

../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

测试

postman

14.jpg

阿里云

16.jpg