当前位置: 首页> 财经> 访谈 > 近期国际热点大事件_贵阳网站设计公司价格_aso优化什么意思是_9 1短视频安装

近期国际热点大事件_贵阳网站设计公司价格_aso优化什么意思是_9 1短视频安装

时间:2025/7/9 4:42:15来源:https://blog.csdn.net/m0_50742275/article/details/145933296 浏览次数:0次
近期国际热点大事件_贵阳网站设计公司价格_aso优化什么意思是_9 1短视频安装

前端(Vue 3)

1. 文件选择与分片上传

首先,我们需要一个文件选择器来选择要上传的文件,并将其分割成多个小块(分片)进行上传。我们还需要记录每个分片的上传状态以便支持断点续传。

<template><div><!-- 文件选择输入框 --><form @submit.prevent="handleSubmit"><input type="file" @change="handleFileChange" ref="fileInput" /><!-- 文件预览信息 --><div v-if="selectedFile"><p>Selected File: {{ selectedFile.name }} ({{ formatBytes(selectedFile.size) }})</p><!-- 进度条显示 --><progress :value="uploadProgress" max="100"></progress></div><!-- 提交按钮 --><button type="submit">Submit</button></form></div>
</template><script>
import { ref, onMounted } from 'vue';
import axios from 'axios';export default {setup() {const fileInput = ref(null);const selectedFile = ref(null);const uploadProgress = ref(0);const uploadedChunks = ref(new Set());const CHUNK_SIZE = 5 * 1024 * 1024; // 每个分片大小为5MB/*** 处理文件选择事件* @param event 文件选择事件*/const handleFileChange = (event) => {selectedFile.value = event.target.files[0];};/*** 格式化字节为可读格式* @param bytes 字节数* @returns {string} 可读格式的文件大小*/const formatBytes = (bytes) => {if (bytes === 0) return '0 Bytes';const k = 1024;const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];};/*** 检查某个分片是否已经上传* @param fileName 文件名* @param chunkNumber 分片编号* @returns {Promise<boolean>}*/const isChunkUploaded = async (fileName, chunkNumber) => {try {const response = await axios.get(`/check-chunk-uploaded?fileName=${fileName}&chunkNumber=${chunkNumber}`);return response.data.uploaded;} catch (error) {console.error('Error checking chunk upload status:', error);return false;}};/*** 上传文件分片* @param chunk 分片数据* @param fileName 文件名* @param chunkNumber 当前分片编号* @param totalChunks 总分片数* @returns {Promise<void>}*/const uploadChunk = async (chunk, fileName, chunkNumber, totalChunks) => {const formData = new FormData();formData.append('file', new Blob([chunk]), fileName);formData.append('chunkNumber', chunkNumber);formData.append('totalChunks', totalChunks);try {const response = await axios.post('/upload', formData, {onUploadProgress: (progressEvent) => {const percentage = (chunkNumber / totalChunks) * 100 + (progressEvent.loaded / progressEvent.total) * (100 / totalChunks);uploadProgress.value = Math.min(percentage, 100);}});if (response.status !== 200) {console.error('Failed to upload chunk');} else {uploadedChunks.value.add(chunkNumber); // 记录已上传的分片}} catch (error) {console.error('Error uploading chunk:', error);}};/*** 合并所有分片* @param fileName 文件名* @param totalChunks 总分片数* @returns {Promise<void>}*/const mergeChunks = async (fileName, totalChunks) => {try {const response = await axios.post('/merge', { fileName, totalChunks });if (response.status === 200) {console.log('All chunks merged successfully.');} else {console.error('Failed to merge chunks.');}} catch (error) {console.error('Error merging chunks:', error);}};/*** 表单提交处理函数* @returns {Promise<void>}*/const handleSubmit = async () => {if (!selectedFile.value) return;const file = selectedFile.value;const totalChunks = Math.ceil(file.size / CHUNK_SIZE);let start = 0;let chunkNumber = 1;while (start < file.size) {const end = Math.min(start + CHUNK_SIZE, file.size);const chunk = file.slice(start, end);if (!(await isChunkUploaded(file.name, chunkNumber))) {await uploadChunk(chunk, file.name, chunkNumber, totalChunks);}start = end;chunkNumber++;}await mergeChunks(file.name, totalChunks);uploadProgress.value = 100;};/*** 加载保存的上传进度*/const loadSavedProgress = () => {const savedProgress = JSON.parse(localStorage.getItem('uploadProgress'));if (savedProgress && savedProgress.fileName === selectedFile.value?.name) {uploadedChunks.value = new Set(savedProgress.uploadedChunks);uploadProgress.value = savedProgress.progress;}};/*** 定时保存上传进度*/const saveUploadProgress = () => {localStorage.setItem('uploadProgress', JSON.stringify({fileName: selectedFile.value?.name,uploadedChunks: Array.from(uploadedChunks.value),progress: uploadProgress.value,}));};onMounted(() => {loadSavedProgress(); // 页面加载时恢复上传进度setInterval(saveUploadProgress, 5000); // 每5秒保存一次上传进度});return {fileInput,selectedFile,uploadProgress,handleFileChange,formatBytes,handleSubmit,};},
};
</script>

后端(Spring Boot + MinIO)

1. 添加依赖

首先,在 pom.xml 中添加 MinIO 的 Maven 依赖:

<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.4.3</version>
</dependency>
2. 配置 MinIO 客户端

在 Spring Boot 应用中配置 MinIO 客户端:

import io.minio.MinioClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MinioConfig {@Beanpublic MinioClient minioClient() {return MinioClient.builder().endpoint("http://localhost:9000") // MinIO 服务地址.credentials("YOUR-ACCESS-KEY", "YOUR-SECRET-KEY") // MinIO 凭证.build();}
}
3. 更新控制器逻辑

更新控制器逻辑以使用 MinIO 客户端上传和合并文件,并提供接口检查分片是否已上传:

import io.minio.*;
import io.minio.errors.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ConcurrentHashMap;@RestController
public class FileUploadController {private static final String BUCKET_NAME = "your-bucket-name"; // 替换为你的桶名称private static final ConcurrentHashMap<String, Integer> chunkCountMap = new ConcurrentHashMap<>();@Autowiredprivate MinioClient minioClient;@PostMapping("/upload")public String uploadFile(@RequestParam("file") MultipartFile file,@RequestParam("chunkNumber") int chunkNumber,@RequestParam("totalChunks") int totalChunks) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {String fileName = file.getOriginalFilename();// 将分片上传到 MinIOminioClient.putObject(PutObjectArgs.builder().bucket(BUCKET_NAME).object(fileName + "_part_" + chunkNumber).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build());// 使用ConcurrentHashMap记录每个文件已上传的分片数量chunkCountMap.merge(fileName, 1, Integer::sum);return "Uploaded chunk " + chunkNumber + " of " + totalChunks;}@GetMapping("/check-chunk-uploaded")public ResponseEntity<?> checkChunkUploaded(@RequestParam String fileName, @RequestParam int chunkNumber) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {boolean exists = minioClient.statObject(StatObjectArgs.builder().bucket(BUCKET_NAME).object(fileName + "_part_" + chunkNumber).build()) != null;return ResponseEntity.ok(new HashMap<String, Boolean>() {{put("uploaded", exists);}});}@PostMapping("/merge")public String mergeChunks(@RequestBody MergeRequest request) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {String fileName = request.getFileName();int totalChunks = request.getTotalChunks();// 创建临时文件用于合并Path tempFilePath = Paths.get(fileName);OutputStream outputStream = new FileOutputStream(tempFilePath.toFile());for (int i = 1; i <= totalChunks; i++) {InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(BUCKET_NAME).object(fileName + "_part_" + i).build());byte[] buffer = new byte[8192];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}inputStream.close();// 删除临时分片文件minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(fileName + "_part_" + i).build());}outputStream.close();// 将合并后的文件上传到 MinIOminioClient.putObject(PutObjectArgs.builder().bucket(BUCKET_NAME).object(fileName).stream(new FileInputStream(tempFilePath.toFile()), Files.size(tempFilePath), -1).contentType("application/octet-stream").build());// 删除本地临时文件Files.delete(tempFilePath);// 清除缓存中的分片计数chunkCountMap.remove(fileName);return "Merged all chunks successfully.";}static class MergeRequest {private String fileName;private int totalChunks;public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public int getTotalChunks() {return totalChunks;}public void setTotalChunks(int totalChunks) {this.totalChunks = totalChunks;}}
}

详细说明

前端部分
  1. 文件选择输入框

    • @change 事件绑定 handleFileChange 函数,用于处理文件选择。
    • ref 属性用于引用文件输入框,方便后续操作。
  2. 文件预览信息

    • 显示选中文件的名称和大小,使用 formatBytes 函数将字节数转换为可读格式。
  3. 进度条显示

    • <progress> 标签用于显示上传进度,value 属性绑定 uploadProgress 变量。
  4. 检查分片是否已上传

    • isChunkUploaded 函数通过调用 /check-chunk-uploaded 接口检查某个分片是否已经上传。
  5. 上传文件分片

    • uploadChunk 函数负责将文件分片上传到服务器。
    • 使用 FormData 对象封装分片数据和其他参数。
    • onUploadProgress 回调函数用于实时更新上传进度。
    • 成功上传后,将分片编号加入 uploadedChunks 集合中。
  6. 合并分片

    • mergeChunks 函数发送合并请求,通知服务器合并所有分片。
  7. 表单提交处理

    • handleSubmit 函数负责计算总分片数,并依次上传每个分片,最后合并分片。
  8. 加载保存的上传进度

    • loadSavedProgress 函数从 localStorage 中加载之前保存的上传进度。
    • saveUploadProgress 函数定时保存当前的上传进度。
后端部分
  1. MinIO 客户端配置

    • 在 MinioConfig 类中配置 MinIO 客户端,设置 MinIO 服务地址和凭证。
  2. 上传文件分片

    • /upload 路由处理文件分片上传请求。
    • 将分片保存到 MinIO 中,使用 PutObjectArgs 构建上传参数。
  3. 检查分片是否已上传

    • /check-chunk-uploaded 路由处理检查分片是否已上传的请求。
    • 使用 statObject 方法检查对象是否存在。
  4. 合并所有分片

    • /merge 路由处理合并请求。
    • 从 MinIO 中读取所有分片文件,按顺序写入最终文件,并删除临时分片文件。
    • 最终将合并后的文件上传到 MinIO 中。
    • 删除本地临时文件并清除缓存中的分片计数。

其他注意事项

  1. MinIO 初始化

    • 确保 MinIO 服务已经启动,并且可以通过提供的 endpoint 访问。
    • 替换 YOUR-ACCESS-KEY 和 YOUR-SECRET-KEY 为你自己的 MinIO 凭证。
  2. 错误处理

    • 在实际应用中,建议增加更多的错误处理机制,例如重试机制、超时处理等,以提高系统的健壮性。
  3. 安全性

    • 在生产环境中,请确保对 MinIO 凭证进行妥善管理,并考虑使用环境变量或配置中心来存储这些敏感信息。
  4. 用户体验优化

    • 提供用户友好的提示信息,如上传进度、成功或失败的消息等。
    • 支持暂停和恢复上传功能,进一步提升用户体验。
关键字:近期国际热点大事件_贵阳网站设计公司价格_aso优化什么意思是_9 1短视频安装

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: