原创 AA专业写后端刘哥 2025-10-11 09:01 重庆
上次产品经理拍我肩膀:“用户要传 10G 的设计图,你数据库存一下?” 我当场掏出对象存储的 “户口本”:这玩意儿才是文件的 “云端豪宅”—— 比本地存储能扛,比数据库能装,还自带 CDN 加速 buff。
点击关注公众号,“技术干货” 及时达!
2. 核心上传代码(像寄快递一样简单)<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>3.15.1</version></dependency>
踩坑提醒:桶权限别设 “公有读”!不然路人能下载你家文件密钥用 RAM 子账号!给最小权限(只给上传权限)四、大文件上传:直接传?怕不是要被测试小姐姐打用户传个 2G 的视频,直接调用上面的代码?结果:网络一波动就断,重传又要从头来,前端还报超时 —— 这就是 “大文件上传噩梦”。解法:「分片上传」!把大文件切成 “小蛋糕”,一块一块传,断了只补缺的。五、分片上传:三步搞定 “大文件搬运”以 100MB 文件为例,切成 10MB / 片,流程像拼乐高:第一步:分片(前端切,后端躺平)前端用public class OssSimpleUpload {// 密钥别硬编码!放配置文件里!(踩坑提醒)private static final String ENDPOINT = "oss-cn-beijing.aliyuncs.com";private static final String ACCESS_KEY = "你的AK";private static final String SECRET_KEY = "你的SK";private static final String BUCKET_NAME = "你的桶名";public static void uploadFile(File file) {// 1. 连接OSS客户端OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY, SECRET_KEY);try {// 2. 上传(桶名+文件路径+文件)ossClient.putObject(BUCKET_NAME, "user-uploads/" + file.getName(), new FileInputStream(file));// 3. 拿访问链接(有效期1小时)Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);String url = ossClient.generatePresignedUrl(BUCKET_NAME, "user-uploads/" + file.getName(), expiration).toString();System.out.println("文件上传成功!链接:" + url);} catch (Exception e) {e.printStackTrace();} finally {// 一定要关客户端!不然连接泄漏(血的教训)ossClient.shutdown();}}}
File.slice()切分,比如:javascript第二步:上传分片(带 “编号” 和 “身份证”)每个分片要带 3 个关键信息:// 10MB一片(10*1024*1024字节)const chunkSize = 10 * 1024 * 1024;const chunks = Math.ceil(file.size / chunkSize);// 切第一片:file.slice(0, chunkSize)
fileMd5:整个文件的哈希值(后面判重有用)chunkIndex:分片编号(0,1,2...)totalChunks:总分片数后端接收后,先存到临时文件夹,比如/tmp/oss-chunks/${fileMd5}/${chunkIndex}第三步:合并分片(OSS 帮你干活)所有分片传完,调用 OSS 的completeMultipartUpload接口合并:java六、灵魂拷问 1:怎么避免重复上传同一个文件?用户手抖传了 3 次同个视频,总不能存 3 份吧?用 “文件身份证”public void mergeChunks(String fileMd5, String fileName, int totalChunks) {OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY, SECRET_KEY);// 1. 初始化分片上传InitiateMultipartUploadResult initResult = ossClient.initiateMultipartUpload(new InitiateMultipartUploadRequest(BUCKET_NAME, "user-uploads/" + fileName));String uploadId = initResult.getUploadId();// 2. 准备分片列表(按编号排序!)List<PartETag> partETags = new ArrayList<>();for (int i = 0; i < totalChunks; i++) {// 从临时文件夹读分片,上传到OSSUploadPartResult uploadResult = ossClient.uploadPart(new UploadPartRequest().withBucketName(BUCKET_NAME).withKey("user-uploads/" + fileName).withUploadId(uploadId).withPartNumber(i + 1) // 分片编号从1开始.withInputStream(new FileInputStream("/tmp/oss-chunks/" + fileMd5 + "/" + i)));partETags.add(uploadResult.getPartETag());}// 3. 通知OSS合并ossClient.completeMultipartUpload(new CompleteMultipartUploadRequest(BUCKET_NAME, "user-uploads/" + fileName, uploadId, partETags));ossClient.shutdown();// 4. 删临时文件(别占空间!)FileUtils.deleteDirectory(new File("/tmp/oss-chunks/" + fileMd5));}
MD5搞定:流程:前端选文件后,先算整个文件的 MD5(大文件可分片算 MD5 再合并,避免卡屏)/check-file?md5=xxx