SpringBoot + Vue2 实现腾讯云 COS 文件上传:从零搭建分片上传系统
本教程完整实现了基于 Spring Boot + Vue2 的腾讯云 COS 文件上传功能,支持:• 普通上传• 大文件分片上传(支持并发、断点续传)• 安全性增强(密钥不暴露)• 跨域规则配置(CORS)可根据实际业务需求进一步扩展,如添加上传进度持久化、上传失败重试机制等。
一、项目目标
实现基于腾讯云 COS 的 大文件分片上传 和 普通文件上传 功能,前后端分离架构,采用 Vue2 + Spring Boot + YML 配置方式,并覆盖以下注意事项:
-
• 分片上传大小限制
-
• 并发上传优化
-
• 断点续传支持
-
• 安全性增强(避免暴露密钥)
-
• 跨域规则配置(CORS)
二、腾讯云 COS 基本配置
1. 创建存储桶
登录腾讯云控制台 → 对象存储 COS → 创建存储桶:
-
• 存储桶名称:
your-bucket-name-1250000000 -
• 地域:
ap-beijing(根据需求选择) -
• 权限设置: 私有读写
2. 获取 API 密钥
前往 API 密钥管理 页面,创建或使用已有 SecretId/SecretKey。
✅ 安全建议:前端禁止直接使用
SecretId/SecretKey,应通过后端代理或使用 STS 临时密钥。
3. 设置跨域规则(CORS)
前往 权限管理 → 跨域规则,添加以下规则:
{
"allowedOrigin":["*"],
"allowedMethod":["GET","POST","PUT","HEAD"],
"allowedHeader":["*"],
"exposeHeader":[],
"maxAgeSeconds":3000
}
三、后端(Spring Boot)实现
1. 依赖配置
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 腾讯云 COS SDK -->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.2.4</version>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
2. 配置腾讯云 COS(application.yml)
tencent:
cos:
secret-id: YOUR_SECRET_ID
secret-key: YOUR_SECRET_KEY
region: ap-beijing
bucket-name: your-bucket-name-1250000000
3. 初始化 COS 客户端
import com.qcloud.cos.COSClient;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.region.Region;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
publicclassCosConfig {
@Value("${tencent.cos.secret-id}")
private String secretId;
@Value("${tencent.cos.secret-key}")
private String secretKey;
@Value("${tencent.cos.region}")
private String region;
@Bean
public COSClient cosClient() {
COSCredentialscred=newBasicCOSCredentials(secretId, secretKey);
RegioncosRegion=newRegion(region);
returnnewCOSClient(cred, cosRegion);
}
}
4. 文件上传接口
1)普通上传
import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.PutObjectRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.UUID;
@RestController
@RequestMapping("/api/upload")
publicclassUploadController {
@Autowired
private COSClient cosClient;
@Value("${tencent.cos.bucket-name}")
private String bucketName;
@PostMapping("/simple")
public String simpleUpload(@RequestParam("file") MultipartFile file) {
try {
StringremoteFileName="uploads/" + UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
InputStreaminputStream= file.getInputStream();
PutObjectRequestrequest=newPutObjectRequest(bucketName, remoteFileName, inputStream, null);
cosClient.putObject(request);
return"https://" + bucketName + ".cos." + cosClient.getClientConfig().getRegion().getName() + ".myqcloud.com/" + remoteFileName;
} catch (Exception e) {
return"上传失败: " + e.getMessage();
}
}
}
2)分片上传接口
① 初始化分片上传
@PostMapping("/init")
public String initMultipartUpload(@RequestParam String fileName) {
try {
InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, fileName);
return cosClient.initiateMultipartUpload(request).getUploadId();
} catch (Exception e) {
return "初始化失败: " + e.getMessage();
}
}
② 上传分片
@PostMapping("/part")
public String uploadPart(
@RequestParam("file") MultipartFile file,
@RequestParam("uploadId") String uploadId,
@RequestParam("partNumber") int partNumber,
@RequestParam("fileName") String fileName) {
try {
UploadPartRequestrequest=newUploadPartRequest()
.withBucketName(bucketName)
.withKey(fileName)
.withUploadId(uploadId)
.withPartNumber(partNumber)
.withInputStream(file.getInputStream())
.withPartSize(file.getSize());
return cosClient.uploadPart(request).getETag(); // 返回 ETag
} catch (Exception e) {
return"分片上传失败: " + e.getMessage();
}
}
③ 合并分片
@PostMapping("/complete")
public String completeMultipartUpload(
@RequestParam("fileName") String fileName,
@RequestParam("uploadId") String uploadId,
@RequestParam("partETags") List<String> partETags) {
try {
List<PartETag> partETagList = partETags.stream()
.map(etag -> newPartETag(partETags.indexOf(etag) + 1, etag))
.toList();
CompleteMultipartUploadRequestrequest=newCompleteMultipartUploadRequest(
bucketName, fileName, uploadId, partETagList
);
cosClient.completeMultipartUpload(request);
return"https://" + bucketName + ".cos." + cosClient.getClientConfig().getRegion().getName() + ".myqcloud.com/" + fileName;
} catch (Exception e) {
return"合并失败: " + e.getMessage();
}
}
四、前端(Vue2)实现
1. 安装依赖
npm install axios
2. 分片上传逻辑
<template>
<div>
<input type="file" @change="handleFileChange" />
<button @click="uploadFile">上传</button>
<div>上传进度: {{ progress }}%</div>
</div>
</template>
<script>
import axios from'axios';
exportdefault {
data() {
return {
file: null,
uploadId: '',
chunkSize: 5 * 1024 * 1024, // 5MB
progress: 0,
uploadedChunks: [], // 已上传的分片索引
};
},
methods: {
handleFileChange(event) {
this.file = event.target.files[0];
this.uploadedChunks = [];
this.progress = 0;
},
asyncuploadFile() {
if (!this.file) {
alert("请选择文件");
return;
}
// 1. 初始化分片上传
const initRes = await axios.post('/api/upload/init', {
fileName: this.file.name,
});
this.uploadId = initRes.data;
// 2. 并发上传分片
const totalChunks = Math.ceil(this.file.size / this.chunkSize);
const promises = [];
for (let i = 0; i < totalChunks; i++) {
const start = i * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
if (this.uploadedChunks.includes(i)) continue; // 跳过已上传分片
const chunk = this.file.slice(start, end);
const formData = newFormData();
formData.append('file', chunk);
formData.append('uploadId', this.uploadId);
formData.append('partNumber', i + 1);
formData.append('fileName', this.file.name);
const promise = axios.post('/api/upload/part', formData, {
onUploadProgress: (progressEvent) => {
const percent = Math.round(
((this.uploadedChunks.length * 100) / totalChunks) +
((progressEvent.loaded / progressEvent.total) * 100 / totalChunks)
);
this.progress = percent;
},
});
promises.push(promise.then(() => {
this.uploadedChunks.push(i); // 标记为已上传
}));
}
// 并发上传
awaitPromise.all(promises);
// 3. 合并分片
const completeRes = await axios.post('/api/upload/complete', {
fileName: this.file.name,
uploadId: this.uploadId,
partETags: [], // 后端应返回每个分片的 ETag
});
alert("上传成功: " + completeRes.data);
},
},
};
</script>
五、注意事项与优化
1. 分片大小限制
-
• 腾讯云要求 单个分片最小 1MB,最大 5GB。
-
• 推荐设置
chunkSize = 5 * 1024 * 1024(5MB)。
2. 并发上传优化
-
• 使用
Promise.all实现并发上传,提升上传效率。 -
• 可设置最大并发数,避免过多请求导致服务器压力过大。
3. 断点续传支持
-
• 通过
this.uploadedChunks记录已上传分片索引。 -
• 重启上传时跳过已上传部分,实现断点续传。
4. 安全性增强
-
• 禁止前端暴露
SecretId/SecretKey。 -
• 建议使用 STS 临时密钥 或 后端代理上传。
-
• 后端可封装上传逻辑,前端仅调用后端接口上传文件。
-
• 使用腾讯云 STS 获取临时密钥,设置访问权限和有效期。
-
六、总结
本教程完整实现了基于 Spring Boot + Vue2 的腾讯云 COS 文件上传功能,支持:
-
• 普通上传
-
• 大文件分片上传(支持并发、断点续传)
-
• 安全性增强(密钥不暴露)
-
• 跨域规则配置(CORS)
可根据实际业务需求进一步扩展,如添加上传进度持久化、上传失败重试机制等。
更多推荐
所有评论(0)