📖 前言
在Web开发中,文件上传是一个常见的需求。本文将带你使用React + TypeScript + Node.js + 阿里云OSS构建一个企业级的文件上传系统。通过本教程,你将掌握阿里云OSS的配置、前端文件上传组件的开发、后端API的实现等核心技能。
�� 第一步:阿里云OSS配置
1. 登录阿里云控制台
访问 阿里云控制台,使用你的阿里云账号登录。
2. 进入RAM访问控制
在控制台搜索"RAM",点击"RAM访问控制"进入用户管理页面。
3. 创建用户
- 点击"创建用户"
- 选择"编程访问"(获取AccessKey)
- 填写用户名,如:oss-upload-user
4. 创建AccessKey
- 创建完成后,点击"创建AccessKey"
- 选择"继续使用AccessKey"
- 保存AccessKey ID和AccessKey Secret(重要!请妥善保管)
5. 配置权限
- 在用户详情页,点击"添加权限"
- 搜索"OSS"并选择"AliyunOSSFullAccess"
- 点击"确定"完成权限配置
6. 创建OSS Bucket
- 在控制台搜索"OSS",进入对象存储服务
- 点击"创建Bucket"
- 选择区域(如:华东1-上海)
- 设置Bucket名称,如:my-upload-bucket
- 其他选项保持默认,点击"确定"
环境变量配置
OSS_REGION=oss-cn-shanghai
OSS_ACCESS_KEY_ID=你的AccessKeyId
OSS_ACCESS_KEY_SECRET=你的AccessKeySecret
OSS_BUCKET_NAME=你的Bucket名称
第二步:后端核心实现
文件验证中间件
const validateFile = async (ctx, next) => {
const file = ctx.request.files?.file;
if (!file) {
ctx.body = { status: 'error', msg: '请选择文件' };
return;
}
// 文件大小限制:10MB
const maxSize = 10 * 1024 * 1024;
if (file.size > maxSize) {
ctx.body = { status: 'error', msg: '文件大小不能超过10MB' };
return;
}
await next();
};
文件上传接口
router.post('/upload', validateFile, async (ctx) => {
try {
const { filepath, originalFilename } = ctx.request.files.file;
// 生成唯一文件名,避免冲突
const timestamp = Date.now();
const uniqueFilename = `uploads/${timestamp}_${originalFilename}`;
const result = await client.put(uniqueFilename, filepath);
ctx.body = {
url: result.url,
status: 'done',
name: uniqueFilename,
uid: timestamp.toString()
};
} catch (error) {
console.error('上传失败:', error);
ctx.body = { status: 'error', msg: '上传失败,请重试' };
}
});
�� 第三步:前端组件实现
核心上传逻辑
const customUpload = async (file: FileType) => {
try {
const formData = new FormData();
formData.append('file', file);
const response = await request.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
const result = response.data;
if (result.status === 'done') {
// 上传成功处理
const newFile = {
...file,
status: 'done',
response: { status: 'done', url: result.url }
} as UploadFile;
setFileList([newFile]);
localStorage.setItem("myavatar", JSON.stringify([newFile]));
onAvatarChange(result.url);
message.success('头像上传成功!');
} else {
message.error(result.msg || '上传失败');
return false;
}
} catch (error) {
console.error('上传失败:', error);
message.error('上传失败,请重试');
return false;
}
return false; // 阻止默认上传
};
// 文件列表变化调用
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => {
setFileList(newFileList);
};
Ant Design
https://ant-design.antgroup.com/index-cn
组件
<>
<ImgCrop rotationSlider>
<Upload
beforeUpload={customUpload}
listType="picture-card"
fileList={fileList}
onPreview={handlePreview}
onChange={handleChange}
maxCount={1}
accept="image/*"
>
{fileList.length >= 1 ? null : <img src={Avatar} alt="" />}
</Upload>
</ImgCrop>
{/* 头像预览 */}
{previewImage && (
<Image
wrapperStyle={{ display: "none" }}
preview={{
visible: previewOpen,
onVisibleChange: (visible) => setPreviewOpen(visible),
afterOpenChange: (visible) => !visible && setPreviewImage(""),
}}
src={previewImage}
/>
)}
</>
🚀 核心特性
- 智能文件验证 - 文件大小、类型检查
- 图片裁剪功能 - 集成antd-img-crop
- 本地缓存优化 - 头像信息本地存储
- 错误处理完善 - 友好的用户提示
💡 技术亮点
- 防重复刷新 - 使用Promise缓存避免重复请求
- 文件命名优化 - 时间戳+原文件名避免冲突
- 用户体验优化 - 实时预览、进度提示
🔒 安全考虑
- 环境变量配置 - 敏感信息不硬编码
- 文件大小限制 - 防止恶意大文件上传
- 权限最小化 - OSS权限精确控制
本期内容就到这里,主页有火柴能点着的干货!!,“真的嘛博主?” 那我就收藏+关注了