前言
- 关于上传文件方式
官网提供: 获取上传的文件,提供了两种模式:File 模式 和 Stream 流模式- 上传文件之后,文件存储方式
第一种存储方式: 存放在你自己的服务器上,和项目代码放在一起;
第二种存储方式: 通过第三方OSS云存储服务,例如:七牛云、阿里云、腾讯云等它们的云存储服务,存放在第三方服务器上;
# 一、Stream 流模式上传文件(单个文件),文件存储在你自己的服务器上
# 1、安装依赖包和插件
npm i await-stream-ready stream-wormhole dayjs --save
// 安装了三个依赖或者插件
// await-stream-ready: 用于处理文件上传的流,当文件上传完毕后,会触发一个事件
// stream-wormhole: 用于处理文件上传的流,当文件上传完毕后,会触发一个事件
// dayjs: 用于处理时间插件
# 2、项目配置:config/config.default.js
//配置上传文件相关
config.multipart = {
fileSize:'50mb', //最大上传大小50MB
mode:'stream', //上传文件模式,stream 流模式 file 模式
fileExtensions:['.xls','.txt','.jpg','.JPG','.png','.PNG',
'.gif','.GIF','.jpeg','.JPEG'],//文件格式自定义
};
# 3、新建控制器 app/controller/upload.js 处理文件上传,前后端都可以用
'use strict';
// 引入
const fs = require('fs');
const path = require('path');
//故名思意 异步二进制 写入流
const awaitWriteStream = require('await-stream-ready').write;
//管道读入一个虫洞
const sendToWormhole = require('stream-wormhole');
// 日期格式化
const dayjs = require('dayjs');
const Controller = require('egg').Controller;
class UploadController extends Controller {
// 上传单个文件流模式到本地服务器
async uploadStreamSingleToServer() {
const fileDirname = 'uploads';
// 单个文件,多个文件查看:https://www.eggjs.org/zh-CN/basics/controller#stream-%E6%A8%A1%E5%BC%8F
const stream = await this.ctx.getFileStream(); //单个文件
// 基础的目录:上传的文件存放的目录,如:'app/public/uploads'
const uploadBasePath = 'app/public/' + fileDirname;
// 生成唯一文件名:时间戳+随机数+文件后缀 [你也可以定义成其它的方式]
const timestamp = Date.now(); //当前时间戳
const random = Number.parseInt(Math.random() * 100000000); //随机数
const extname = path.extname(stream.filename).toLocaleLowerCase(); //文件后缀
const filename = `${timestamp}_${random}${extname}`;
// 生成文件夹如:app/public/uploads后面的文件夹如:/2019/01/01,当然你可以自定义其他方式
// const dirname = dayjs(Date.now()).format('YYYY/MM/DD');//使用我们上面安装的dayjs模块方法转换的日期格式
const dirname = dayjs(Date.now()).format('YYYYMMDD');// /20190101
//这个方法是判断这个文件夹是否存在,不存在则创建这个文件夹
function mkdirsSync(dirname) {
if(fs.existsSync(dirname)){
return true
} else {
if(mkdirsSync(path.dirname(dirname))){
fs.mkdirSync(dirname)
return true
}
}
}
//调用方法生成完整文件路径
mkdirsSync(path.join(uploadBasePath,dirname));
// 生成写入路径: 完整文件路径 + 文件名
const target = path.join(uploadBasePath,dirname,filename);
// 写入流
const writeStream = fs.createWriteStream(target);
// 尝试写入文件数据流
try {
// 异步把文件流写入
await awaitWriteStream(stream.pipe(writeStream));
} catch (error) {
// 出现错误,关闭管道
await sendToWormhole(stream);
this.ctx.throw(500,error);
}
//写入成功后返回文件路径地址
let url = path.join('/public/' + fileDirname,dirname,filename).replace(/\\|\//g,'/');
this.ctx.apiSuccess({ url });
}
}
module.exports = UploadController;
# 4、配置路由 app/router.js
// 上传单个文件流模式到本地服务器
router.post('/uploadStreamSingleToServer', controller.upload.uploadStreamSingleToServer);
# 5、如果不需要管理员登录就可以上传图片,配置中间件路由 config/config.default.js
// 对中间件adminAuth进一步配置
config.adminAuth = {
ignore: [
...,
"/uploadStreamSingleToServer", //忽略文件上传路由需要管理员登录
],
};
# 6、postman测试上传文件
- post请求,地址:
http://127.0.0.1:7001/uploadStreamSingleToServer- Body发送数据:
form-data类型,Key值:files , Value:选择要上传的图片- 返回结果示例:
{ "msg": "ok", "data": { "url": "/public/uploads/20240309/1709952961932_9076609.jpg" } }我们上面这个方法基本已经能满足需求了,这里给大家扩展一下思路,就是如果你想自定义上传的文件路径,我们给大家扩展了下面这个方法,上传文件代码基本一样,就是对路径做了处理,因此这里就不讲代码了,感兴趣的同学,可以看一下这个方法,代码每一步注释写的也很清楚,具体代码如下:
# 7、扩展: 自定义上传文件路径
app/controller/upload.js// 自定义上传路径上传单个文件流模式到本地服务器 async uploadStreamSingleToServerDiy() { //自定义文件都是放在uploads目录下的Diy文件夹下 const fileDirname = 'uploads/Diy'; // 基础的目录:上传的文件存放的目录,如:'app/public/uploads/Diy' let uploadBasePath = 'app/public/' + fileDirname; // 单个文件,多个文件查看:https://www.eggjs.org/zh-CN/basics/controller#stream-%E6%A8%A1%E5%BC%8F const stream = await this.ctx.getFileStream(); //单个文件 // 根据传参得到用户自定义文件夹,多层文件夹用下划线传递参数, const { protocol, host } = this.ctx.request; let mydir = this.ctx.params.diydir;//console.log('接收的参数',mydir); //如果存在参数 if(mydir){ //将下划线转化为路径,如参数:mysourse_myfile,则转成 /mysourse/myfile文件夹 // 一般给管理员或者用户使用,自定义参数一般是:管理员或者用户账号 let mydirStr = ''; let mydirArr = mydir.split('_'); for(let i=0;i<mydirArr.length;i++){ mydirStr += '/' + mydirArr[i]; } mydir = mydirStr; //如果传参存在自定义文件夹,参数如:mysourse_myfile //则自定义文件夹路径如:app/public/uploads/Diy/mysourse/myfile uploadBasePath += mydir; } // 生成唯一文件名:时间戳+随机数+文件后缀 [你也可以定义成其它的方式] const timestamp = Date.now(); //当前时间戳 const random = Number.parseInt(Math.random() * 100000000); //随机数 const extname = path.extname(stream.filename).toLocaleLowerCase(); //文件后缀 const filename = `${timestamp}_${random}${extname}`; // 生成文件夹如:app/public/uploads后面的文件夹如:/2019/01/01,当然你可以自定义其他方式 // const dirname = dayjs(Date.now()).format('YYYY/MM/DD');//使用我们上面安装的dayjs模块方法转换的日期格式 const dirname = dayjs(Date.now()).format('YYYYMMDD');// /20190101 //这个方法是判断这个文件夹是否存在,不存在则创建这个文件夹 function mkdirsSync(dirname) { if(fs.existsSync(dirname)){ return true } else { if(mkdirsSync(path.dirname(dirname))){ fs.mkdirSync(dirname) return true } } } //调用方法生成完整文件路径 mkdirsSync(path.join(uploadBasePath,dirname)); // 生成写入路径: 完整文件路径 + 文件名 const target = path.join(uploadBasePath,dirname,filename); // 写入流 const writeStream = fs.createWriteStream(target); // 尝试写入文件数据流 try { // 异步把文件流写入 await awaitWriteStream(stream.pipe(writeStream)); } catch (error) { // 出现错误,关闭管道 await sendToWormhole(stream); this.ctx.throw(500,error); } //写入成功后返回文件路径地址,注意访问路径开始不能是 app/ //即 uploadBasePath变量如:app/public/uploads/Diy/mysourse/myfile //去掉app,返回:/public/uploads/Diy/mysourse/myfile let mydir_base = uploadBasePath.substring(3); let url = path.join(mydir_base, dirname, filename).replace(/\\|\//g, '/'); // url = protocol + '://' + host + url; //如果想带上域名加上这句话 this.ctx.apiSuccess({ url }); }# 使用说明:
- 定义路由:
app/router.js// 自定义上传路径上传单个文件流模式到本地服务器 router.post('/uploadStreamSingleToServerDiy/:diydir', controller.upload.uploadStreamSingleToServerDiy);
- 如果希望不登录也可以使用这个方法,同样需要再配置文件里面忽略掉权限验证
config/config.default.js// 对中间件adminAuth进一步配置 config.adminAuth = { ignore: [ ..., "/uploadStreamSingleToServer", // 上传单个文件流模式到本地服务器 "/uploadStreamSingleToServerDiy",// 自定义上传路径上传单个文件流模式到本地服务器 ], };
这个方法我们规定的基本路径是:
/public/uploads/Diy文件夹下,去自定义文件夹;这个方法虽然可以自定义参数生成文件夹,但是建议使用用户的账号作为参数,这样存储的文件就是以用户的账号分类的;
# 5. 具体使用方法:
一. 请求类型:POST
二. 请求路由地址方式一,自定义一个文件夹:http://127.0.0.1:7001/uploadStreamSingleToServerDiy/myuser01参数建议用户或者管理员账号,返回的结果如下:{ "msg": "ok", "data": { "url": "/public/uploads/Diy/myuser01/20240310/1710038202074_99501763.png" } }三. 请求路由地址方式二,自定义多个文件夹进行嵌套:
http://127.0.0.1:7001/uploadStreamSingleToServerDiy/mysourse_myfile参数中多个文件夹名字必须用下划线隔开,返回的结果如下:{ "msg": "ok", "data": { "url": "/public/uploads/Diy/mysourse/myfile/20240310/1710038456814_52001040.png" } }
# 二、File 模式
将在后面的课程:视频点播项目中使用
# 三、上传文件(图片)到阿里云存储OSS
# 1. 配置阿里云存储,拿到密钥和bucket等信息
来到阿里云控制台: 控制台 -> 对象存储 -> OSS
- 阿里云的云存储OSS默认是私有权限,创建完
Bucket之后,可在Bucket列表->找到创建的Bucket->权限控制->阻止公共访问【关闭】->读写权限->公共读写概览->访问端口栏目->配置Bucket信息- 如何查看或设置
accessId及accessSecret
权限控制->访问控制RAM->前往RAM控制台身份管理->用户组->创建用户组① 用户组名称:
OSS
② 显示名称:OSS操作组
③ 备注:OSS操作组,用于测试文件上传、查看等
- 创建完成之后,进入刚刚创建的用户组
①
权限管理->新增授权->搜索 oss-> 将oss相关权限勾上AliyunOSSFullAccess,AliyunOSSReadOnlyAccess
②身份管理->用户->创建用户用户账号信息:
- 登录名称:
thinkphp_eggjs- 显示名称:
thinkphp_eggjs两套框架上传图片等文件
访问方式:- 选择用
使用永久 AccessKey 访问创建 AccessKey ID 和 AccessKey Secret,支持通过 API 或其他开发工具访问
创建成功后,可复制AccessKey ID和AccessKey Secret,用于配置文件系统
选中创建的用户->添加到用户组->OSS操作组->确定
# 2. 兼容多版本eggjs,安装上传的插件命令
安装:
// 安装`egg-multipart`插件,ctx.multipart() 方法由该插件提供:
// egg-multipart@2 # 指定兼容版本
npm install egg-multipart@2 --save
// 安装阿里云OSS SDK命名:
npm install ali-oss --save
// 安装命名:
npm install ali-oss moment stream-wormhole --save
启用插件 (config/plugin.js):
启用 egg-multipart 插件:ctx.multipart() 方法由该插件提供
multipart: {
enable: true,
package: 'egg-multipart'
},
引入:
// 阿里云OSS SDK
const OSS = require('ali-oss');
// node系统模块
const path = require('path');
const fs = require('fs');
// 上传插件
const sendToWormhole = require('stream-wormhole');
const moment = require('moment');
# 四、上传文件(图片)到阿里云存储OSS--File 模式(完整流程和代码)
# 1. 环境说明
运行环境最低版本为以下的版本。
`node -v`:v18.12.1 `node版本`
`npm list egg`:
egg-mock@5.10.9
│ └── egg@3.17.5 deduped
└── egg@3.17.5 `egg版本`
`npm -v`:8.19.2 `npm版本`
# 2. 安装阿里云OSS SDK命令
npm install ali-oss --save
# 3. 路由
app/router/admin/shop.js
// 图片上传阿里云
router.post('/shop/admin/image/uploadAliyun',controller.admin.image.uploadAliyunOSS);
# 4.配置:config/config.default.js
...
//配置上传文件相关
config.multipart = {
fileSize: '50mb', //最大上传大小50MB
mode: 'file', //上传文件模式,stream 流模式 file 模式
fileExtensions: ['.xls', '.txt', '.jpg', '.JPG', '.png', '.PNG',
'.gif', '.GIF', '.jpeg', '.JPEG',
'.doc', '.docx', '.pdf', '.PDF', '.xlsx', '.XLSX'],//文件格式自定义
cleanSchedule: {
cron: '0 30 4 * * *', // 每天凌晨4:30清理临时文件
},
};
// 上传文件到阿里云oss的配置
config.oss = {
client: {
accessKeyId: '你自己的阿里云oss的accessKeyId',
accessKeySecret: '你自己的阿里云oss的accessKeySecret',
bucket: 'thinkphp-eggjs', //你的bucket名称
endpoint: 'oss-cn-hangzhou.aliyuncs.com', //你的bucket所在地域
timeout: '60s', //上传文件超时时间
}
};
...
# 5. 扩展方法
app/extend/context.js
'use strict';
// 阿里云OSS SDK
const OSS = require('ali-oss');
// node系统模块
const path = require('path');
const fs = require('fs/promises');
const crypto = require('crypto');
module.exports = {
...
// 通用文件上传到阿里云OSS方法--File模式
/**
* 通用文件上传到阿里云OSS方法--File模式
* @param {string} fieldName - 上传文件的字段名
* @param {number} imageClassId - 图片分类ID,默认为0
* @param {string} prefix - 阿里云oss的Bucket中最外层文件夹名称,默认为'images'
* @returns {Array} - 上传结果数组,每个元素对象包含上传文件的URL、路径、分类ID和创建时间
*/
async uploadOSS_File(fieldName, imageClassId = 0, prefix = 'images') {
const { app } = this;
try {
// 兼容 Egg 3.x 的文件结构
if (!this.request.files || this.request.files.length === 0) {
throw new Error('请选择要上传的文件');
}
// 筛选指定字段的文件
const matchedFiles = this.request.files.filter(
f => f.fieldname === fieldName
);
if (matchedFiles.length === 0) {
throw new Error(`未找到 ${fieldName} 字段的上传文件`);
}
// 统一处理为数组
const fileList = matchedFiles;
// 创建 OSS 客户端
const client = new OSS(app.config.oss.client);
// 并行上传处理
const results = await Promise.all(
fileList.map(async file => {
try {
// 生成唯一文件名
const timestamp = Date.now();
const randomStr = crypto.randomBytes(6).toString('hex');
const extname = path.extname(file.filename).toLowerCase();
const filename = `${timestamp}_${randomStr}${extname}`;
// 创建日期路径
const now = new Date();
const datePath = [
now.getFullYear(),
String(now.getMonth() + 1).padStart(2, '0'),
String(now.getDate()).padStart(2, '0')
].join('');
// 构造完整路径
const ossPath = `${prefix}/${datePath}/${filename}`;
// 读取文件内容
const fileContent = await fs.readFile(file.filepath);
// 上传到 OSS
const ossRes = await client.put(ossPath, fileContent);
return {
url: ossRes.url,
path: ossPath,
image_class_id: Number(imageClassId),
create_time: Math.floor(Date.now() / 1000)
};
} finally {
// 清理临时文件
await fs.unlink(file.filepath).catch(() => {});
}
})
);
return results;
} catch (e) {
// 全局异常清理
if (this.request.files) {
await this.cleanupRequestFiles();
}
throw e;
}
},
};
# 6. 控制器
app/controller/admin/image.js
...
class ImageController extends Controller {
// 通用文件上传到阿里云OSS方法
async uploadAliyunOSS() {
const { ctx,app } = this;
try {
// 获取分类ID(通过字段传递)
const imageClassId = ctx.request.body.image_class_id || 0;
// 通用文件上传到阿里云OSS方法--File模式
const result = await ctx.uploadOSS_File('img', imageClassId, 'images');
ctx.body = {
code: 200,
msg: '上传成功',
data: result
};
} catch (error) {
ctx.status = 500;
ctx.body = {
code: 500,
msg: error.message,
data: []
};
}
}
}
...
# 7.postman测试方法
# 1. 单图上传测试:
方法:
POST
URL:http://127.0.0.1:7001/shop/admin/image/uploadAliyun
Body选择form-data格式:
添加字段:
Key:image_class_id,Value:0(类型Text)
Key:img,Value: 选择文件(类型File)
# 2. 多图上传测试:
方法:
POST
URL:http://127.0.0.1:7001/shop/admin/image/uploadAliyun
Body选择form-data格式:
添加字段:
Key:image_class_id,Value:0(类型Text)
Key:img,Value: 选择多个文件(点击字段右侧下拉箭头选择类型File后,可多选文件)
或者 填写多个img字段,每个字段选择一个文件(类型File)
# 8. 返回实例
// 单图
{
"code": 200,
"msg": "上传成功",
"data": [
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866732481_20f90b51544f.png",
"path": "images/20250417/1744866732481_20f90b51544f.png",
"image_class_id": 0,
"create_time": 1744866732
}
]
}
// 多图
{
"code": 200,
"msg": "上传成功",
"data": [
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_f376ca377d65.png",
"path": "images/20250417/1744866760867_f376ca377d65.png",
"image_class_id": 0,
"create_time": 1744866761
},
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_85681992b9b1.png",
"path": "images/20250417/1744866760867_85681992b9b1.png",
"image_class_id": 0,
"create_time": 1744866761
},
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_5aab3128bc21.png",
"path": "images/20250417/1744866760867_5aab3128bc21.png",
"image_class_id": 0,
"create_time": 1744866761
}
]
}
# 五、上传文件(图片)到阿里云存储OSS--Stream 流模式(完整流程和代码)
# 1. 环境说明
运行环境最低版本为以下的版本。
`node -v`:v18.12.1 `node版本`
`npm list egg`:
egg-mock@5.10.9
│ └── egg@3.17.5 deduped
└── egg@3.17.5 `egg版本`
`npm -v`:8.19.2 `npm版本`
# 2. 安装阿里云OSS SDK命令
npm install ali-oss --save
# 3. 路由
app/router/admin/shop.js
// 图片上传阿里云
router.post('/shop/admin/image/uploadAliyun',controller.admin.image.uploadAliyunOSS);
# 4.配置:config/config.default.js
...
//配置上传文件相关
config.multipart = {
fileSize: '50mb', //最大上传大小50MB
mode: 'stream', //上传文件模式,stream 流模式 file 模式
fileExtensions: ['.xls', '.txt', '.jpg', '.JPG', '.png', '.PNG',
'.gif', '.GIF', '.jpeg', '.JPEG',
'.doc', '.docx', '.pdf', '.PDF', '.xlsx', '.XLSX'],//文件格式自定义
cleanSchedule: {
cron: '0 30 4 * * *', // 每天凌晨4:30清理临时文件
},
};
// 上传文件到阿里云oss的配置
config.oss = {
client: {
accessKeyId: '你自己的阿里云oss的accessKeyId',
accessKeySecret: '你自己的阿里云oss的accessKeySecret',
bucket: 'thinkphp-eggjs', //你的bucket名称
endpoint: 'oss-cn-hangzhou.aliyuncs.com', //你的bucket所在地域
timeout: '60s', //上传文件超时时间
}
};
...
# 5. 扩展方法
app/extend/context.js
'use strict';
// 阿里云OSS SDK
const OSS = require('ali-oss');
// node系统模块
const path = require('path');
const crypto = require('crypto');
const fs = require('fs');
//使用 pump 确保流正确写入,必须写入临时文件后才能上传到 OSS
//内存管理优化,使用 pump 控制流式写入,每个文件单独处理,避免内存峰值,及时清理临时文件
const pump = require('mz-modules/pump');
module.exports = {
...
// 通用文件上传到阿里云OSS方法--Stream 流模式
/**
* 通用文件上传到阿里云OSS方法--Stream 流模式
* @param {string} fieldName - 上传文件的字段名
* @param {number} imageClassId - 图片分类ID,默认为0
* @param {string} prefix - 阿里云oss的Bucket中最外层文件夹名称,默认为'images'
* @returns {Array} - 上传结果数组,每个元素对象包含上传文件的URL、路径、分类ID和创建时间
*/
async uploadOSS_Stream(fieldName, imageClassId = 0, prefix = 'images') {
const { app } = this;
const client = new OSS(app.config.oss.client);
const results = [];
const tmpFiles = []; // 记录临时文件路径
try {
//通过 ctx.multipart() 创建迭代器,循环处理每个上传的文件流
const multipart = this.multipart();
let stream;
// 流式迭代处理
while ((stream = await multipart()) != null) {
// 过滤非目标字段,精确过滤目标上传字段,支持与其他表单字段共存
if (stream.fieldname !== fieldName) {
continue;
}
// 生成唯一文件名
const timestamp = Date.now();
const randomStr = crypto.randomBytes(6).toString('hex');
const extname = path.extname(stream.filename).toLowerCase();
const filename = `${timestamp}_${randomStr}${extname}`;
// 创建日期目录
const now = new Date();
const datePath = [
now.getFullYear(),
String(now.getMonth() + 1).padStart(2, '0'),
String(now.getDate()).padStart(2, '0')
].join('');
// 构造完整路径
const ossPath = `${prefix}/${datePath}/${filename}`;
// 创建临时文件路径
const tmpFilePath = path.join(
app.config.multipart.tmpdir,
`${timestamp}_${randomStr}${extname}`
);
tmpFiles.push(tmpFilePath);
// 写入临时文件,使用 pump 确保流正确写入,必须写入临时文件后才能上传到 OSS
const writeStream = fs.createWriteStream(tmpFilePath);
await pump(stream, writeStream);
// 上传到OSS,直接从临时文件创建可读流上传,避免内存中缓存大文件
const ossRes = await client.put(
ossPath,
fs.createReadStream(tmpFilePath)
);
results.push({
url: ossRes.url,
path: ossPath,
image_class_id: Number(imageClassId),
create_time: Math.floor(Date.now() / 1000)
});
}
// 清理临时文件
await Promise.all(
tmpFiles.map(file => fs.promises.unlink(file))
);
return results;
} catch (err) {
// 异常时清理所有临时文件
await Promise.all(
tmpFiles.map(file =>
fs.promises.unlink(file).catch(() => {})
)
);
throw err;
}
},
};
# 6. 控制器
app/controller/admin/image.js
...
class ImageController extends Controller {
// 通用文件上传到阿里云OSS方法
async uploadAliyunOSS() {
const { ctx,app } = this;
try {
// 获取分类ID(通过字段传递)
const imageClassId = ctx.request.body.image_class_id || 0;
// 通用文件上传到阿里云OSS方法--File模式
// const result = await ctx.uploadOSS_File('img', imageClassId, 'images');
// 通用文件上传到阿里云OSS方法--Stream 流模式
const result = await ctx.uploadOSS_Stream('img', imageClassId, 'images');
ctx.body = {
code: 200,
msg: '上传成功',
data: result
};
} catch (error) {
ctx.status = 500;
ctx.body = {
code: 500,
msg: error.message,
data: []
};
}
}
}
...
# 7.postman测试方法
# 1. 单图上传测试:
方法:
POST
URL:http://127.0.0.1:7001/shop/admin/image/uploadAliyun
Body选择form-data格式:
添加字段:
Key:image_class_id,Value:0(类型Text)
Key:img,Value: 选择文件(类型File)
# 2. 多图上传测试:
方法:
POST
URL:http://127.0.0.1:7001/shop/admin/image/uploadAliyun
Body选择form-data格式:
添加字段:
Key:image_class_id,Value:0(类型Text)
Key:img,Value: 选择多个文件(点击字段右侧下拉箭头选择类型File后,可多选文件)
或者 填写多个img字段,每个字段选择一个文件(类型File)
# 8. 返回实例
// 单图
{
"code": 200,
"msg": "上传成功",
"data": [
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866732481_20f90b51544f.png",
"path": "images/20250417/1744866732481_20f90b51544f.png",
"image_class_id": 0,
"create_time": 1744866732
}
]
}
// 多图
{
"code": 200,
"msg": "上传成功",
"data": [
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_f376ca377d65.png",
"path": "images/20250417/1744866760867_f376ca377d65.png",
"image_class_id": 0,
"create_time": 1744866761
},
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_85681992b9b1.png",
"path": "images/20250417/1744866760867_85681992b9b1.png",
"image_class_id": 0,
"create_time": 1744866761
},
{
"url": "http://thinkphp-eggjs.oss-cn-hangzhou.aliyuncs.com/images/20250417/1744866760867_5aab3128bc21.png",
"path": "images/20250417/1744866760867_5aab3128bc21.png",
"image_class_id": 0,
"create_time": 1744866761
}
]
}
# 9.常见问题解决方案
# 1. 上传大文件时内存溢出
// config/config.default.js
config.multipart = {
// 其他配置...
fileModeMatch: null // 禁用文件模式匹配
};
# 2. 临时文件清理失败
// 在 app.js 中添加全局清理
app.beforeStart(async () => {
const tmpdir = app.config.multipart.tmpdir;
setInterval(() => {
fs.readdir(tmpdir, (err, files) => {
files.forEach(file => {
fs.unlink(path.join(tmpdir, file), () => {});
});
});
}, 3600 * 1000); // 每小时清理一次
});
# 3. 上传速度优化
// 使用分片上传
const ossRes = await client.multipartUpload(ossPath, tmpFilePath, {
parallel: 4,
partSize: 1024 * 1024
});