Cloud Computing 12 min read

Implement Chunked Large File Upload with Tencent Cloud COS, Vue2 & Spring Boot

This guide demonstrates how to configure Tencent Cloud COS for both simple and multipart large‑file uploads in a front‑end/back‑end separated architecture, using Vue2 on the client side and Spring Boot with YML configuration on the server, covering bucket setup, security, CORS, concurrency, and resumable upload techniques.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Implement Chunked Large File Upload with Tencent Cloud COS, Vue2 & Spring Boot
图片
图片

1. Project Goal

Implement large file chunked upload and normal file upload based on Tencent Cloud COS, using a front‑back separation architecture with Vue2 + Spring Boot + YML configuration, covering the following points:

Chunk size limit

Concurrent upload optimization

Resumable upload support

Security enhancement (avoid exposing secret keys)

CORS configuration

2. Tencent Cloud COS Basic Configuration

1. Create Bucket

Login to Tencent Cloud console → Object Storage COS → Create bucket:

Bucket name: your-bucket-name-1250000000 Region: ap-beijing (choose as needed)

Permission: Private read/write

2. Obtain API Keys

Go to API key management page and create or use existing SecretId/SecretKey.

✅ Security tip: Do not use SecretId/SecretKey directly in the front end; use a back‑end proxy or STS temporary credentials.

3. Set CORS Rules

Navigate to Permission Management → CORS Rules and add:

{
  "allowedOrigin": ["*"],
  "allowedMethod": ["GET","POST","PUT","HEAD"],
  "allowedHeader": ["*"],
  "exposeHeader": [],
  "maxAgeSeconds": 3000
}

3. Backend (Spring Boot) Implementation

1. Dependency Configuration

<dependencies>
  <!-- Spring Boot Web -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <!-- Tencent Cloud COS SDK -->
  <dependency>
    <groupId>com.qcloud</groupId>
    <artifactId>cos_api</artifactId>
    <version>5.2.4</version>
  </dependency>

  <!-- Utility -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
</dependencies>

2. Configure COS in application.yml

tencent:
  cos:
    secret-id: YOUR_SECRET_ID
    secret-key: YOUR_SECRET_KEY
    region: ap-beijing
    bucket-name: your-bucket-name-1250000000

3. Initialize COS Client

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
public class CosConfig {
  @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() {
    COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
    Region cosRegion = new Region(region);
    return new COSClient(cred, cosRegion);
  }
}

4. File Upload API

1) Simple Upload

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")
public class UploadController {
  @Autowired
  private COSClient cosClient;

  @Value("${tencent.cos.bucket-name}")
  private String bucketName;

  @PostMapping("/simple")
  public String simpleUpload(@RequestParam("file") MultipartFile file) {
    try {
      String remoteFileName = "uploads/" + UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
      InputStream inputStream = file.getInputStream();
      PutObjectRequest request = new PutObjectRequest(bucketName, remoteFileName, inputStream, null);
      cosClient.putObject(request);
      return "https://" + bucketName + ".cos." + cosClient.getClientConfig().getRegion().getName() + ".myqcloud.com/" + remoteFileName;
    } catch (Exception e) {
      return "Upload failed: " + e.getMessage();
    }
  }
}

2) Multipart Upload

① Initialize multipart upload

@PostMapping("/init")
public String initMultipartUpload(@RequestParam String fileName) {
  try {
    InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, fileName);
    return cosClient.initiateMultipartUpload(request).getUploadId();
  } catch (Exception e) {
    return "Initialization failed: " + e.getMessage();
  }
}

② Upload part

@PostMapping("/part")
public String uploadPart(@RequestParam("file") MultipartFile file,
                         @RequestParam("uploadId") String uploadId,
                         @RequestParam("partNumber") int partNumber,
                         @RequestParam("fileName") String fileName) {
  try {
    UploadPartRequest request = new UploadPartRequest()
        .withBucketName(bucketName)
        .withKey(fileName)
        .withUploadId(uploadId)
        .withPartNumber(partNumber)
        .withInputStream(file.getInputStream())
        .withPartSize(file.getSize());
    return cosClient.uploadPart(request).getETag();
  } catch (Exception e) {
    return "Part upload failed: " + e.getMessage();
  }
}

③ Complete multipart upload

@PostMapping("/complete")
public String completeMultipartUpload(@RequestParam("fileName") String fileName,
                                    @RequestParam("uploadId") String uploadId,
                                    @RequestParam List<String> partETags) {
  try {
    List<PartETag> partETagList = partETags.stream()
        .map(etag -> new PartETag(partETags.indexOf(etag) + 1, etag))
        .toList();
    CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(bucketName, fileName, uploadId, partETagList);
    cosClient.completeMultipartUpload(request);
    return "https://" + bucketName + ".cos." + cosClient.getClientConfig().getRegion().getName() + ".myqcloud.com/" + fileName;
  } catch (Exception e) {
    return "Merge failed: " + e.getMessage();
  }
}

4. Frontend (Vue2) Implementation

1. Install Dependency

npm install axios

2. Chunked Upload Logic

<template>
  <div>
    <input type="file" @change="handleFileChange"/>
    <button @click="uploadFile">Upload</button>
    <div>Upload progress: {{ progress }}%</div>
  </div>
</template>

<script>
import axios from 'axios';
export default {
  data() {
    return {
      file: null,
      uploadId: '',
      chunkSize: 5 * 1024 * 1024,
      progress: 0,
      uploadedChunks: []
    };
  },
  methods: {
    handleFileChange(event) {
      this.file = event.target.files[0];
      this.uploadedChunks = [];
      this.progress = 0;
    },
    async uploadFile() {
      if (!this.file) {
        alert('Please select a file');
        return;
      }
      const initRes = await axios.post('/api/upload/init', { fileName: this.file.name });
      this.uploadId = initRes.data;
      const totalChunks = Math.ceil(this.file.size / this.chunkSize);
      const promises = [];
      for (let i = 0; i < totalChunks; i++) {
        if (this.uploadedChunks.includes(i)) continue;
        const start = i * this.chunkSize;
        const end = Math.min(start + this.chunkSize, this.file.size);
        const chunk = this.file.slice(start, end);
        const formData = new FormData();
        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: (e) => {
            const percent = Math.round(((this.uploadedChunks.length * 100) / totalChunks) + ((e.loaded / e.total) * 100 / totalChunks));
            this.progress = percent;
          }
        }).then(() => {
          this.uploadedChunks.push(i);
        });
        promises.push(promise);
      }
      await Promise.all(promises);
      const completeRes = await axios.post('/api/upload/complete', {
        fileName: this.file.name,
        uploadId: this.uploadId,
        partETags: [] // backend should return ETags
      });
      alert('Upload successful: ' + completeRes.data);
    }
  }
};
</script>

5. Notes and Optimizations

1. Chunk Size Limits

Tencent Cloud requires each part to be at least 1 MB and at most 5 GB.

Recommended setting: chunkSize = 5 * 1024 * 1024 (5 MB).

2. Concurrent Upload Optimization

Use Promise.all to upload parts concurrently and improve efficiency.

Limit maximum concurrency to avoid excessive server load.

3. Resumable Upload Support

Track uploaded parts with this.uploadedChunks to skip already uploaded chunks.

On restart, continue from the last successful part.

4. Security Enhancements

Never expose SecretId/SecretKey on the front end.

Prefer STS temporary credentials or back‑end proxy for uploading.

6. Summary

This tutorial fully implements Tencent Cloud COS file upload with Spring Boot and Vue2, supporting simple upload, large‑file multipart upload with concurrency and resumable capabilities, enhanced security, and CORS configuration.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring Bootfile uploadTencent Cloud COSchunked uploadVue2
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.