分片上传怎么优化,怎么让存在不同服务器上的分片进行快速合

分片上传怎么优化,怎么让存在不同服务器上的分片进行快速合

前端

前端将文件分为多个分片,然后通过Promise.all并行上传多个分片,这样可以最大的利用宽带资源。

断点续传 在上传过程中,如果某个分片上传失败,能够记录当前的上传记录,并在回复上传时仅回复上传失败的分片.

上传进度管理: 试试反馈上传进度,提升用户体验,每个分片上传成功后,将进度展示给用户。

js 复制代码
async function uploadFile(file) {
    const chunkSize = 5 * 1024 * 1024; // 每个分片的大小为5MB
    const totalChunks = Math.ceil(file.size / chunkSize);
    const fileHash = await calculateFileHash(file); // 计算文件的哈希值
    const uploadedChunks = await checkUploadedChunks(fileHash); // 查询已上传的分片
    const promises = [];
    let uploadedSize = 0;

    for (let i = 0; i < totalChunks; i++) {
        if (uploadedChunks.includes(i)) {
            // 如果该分片已经上传,跳过它并更新已上传大小
            uploadedSize += chunkSize;
            continue;
        }

        const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
        const formData = new FormData();
        formData.append('file', chunk);
        formData.append('index', i);
        formData.append('total', totalChunks);
        formData.append('fileHash', fileHash);

        // 添加上传任务到 promises 数组中
        promises.push(uploadChunk(formData, chunk.size, uploadedSize, file.size));
        uploadedSize += chunk.size;
    }

    // 并行上传所有分片
    await Promise.all(promises);

    // 触发服务器上的合并操作
    await mergeChunks(file.name, fileHash);
}

function uploadChunk(formData, chunkSize, uploadedSize, fileSize) {
    return axios.post('/upload_chunk', formData, {
        onUploadProgress: (progressEvent) => {
            const chunkProgress = (progressEvent.loaded / chunkSize) * 100;
            const overallProgress = ((uploadedSize + progressEvent.loaded) / fileSize) * 100;
            console.log(`Chunk progress: ${chunkProgress.toFixed(2)}%, Overall progress: ${overallProgress.toFixed(2)}%`);
        }
    }).catch(error => {
        console.error(`Failed to upload chunk: ${error}`);
        // 处理失败情况,这里可以加入重试机制
    });
}

function mergeChunks(fileName, fileHash) {
    return axios.post('/merge_chunks', { fileName, fileHash });
}

async function calculateFileHash(file) {
    return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        const spark = new SparkMD5.ArrayBuffer();

        fileReader.onload = (event) => {
            spark.append(event.target.result);
            resolve(spark.end());
        };

        fileReader.onerror = (event) => {
            reject(event);
        };

        fileReader.readAsArrayBuffer(file);
    });
}

async function checkUploadedChunks(fileHash) {
    const response = await axios.get(`/upload/status?fileHash=${fileHash}`);
    return response.data.uploadedChunks; // 返回已经上传的分片索引数组
}

后端

分片元数据管理

分片元数据管理

在每个分片上传时,记录其元数据(如分片顺序、服务器位置、分片大小等)。可以使用数据库或分布式缓存(Redis)来管理这些元数据,便于后续的合并操作。

存储位置优化

根据分片的大小和服务器负载情况, 动态选择分片的存储服务器,确保服务器间的负载均衡。

分布式存储与合并

Plant A 服务器间的直接传输

在合并分片的时候,可以使用服务器间的直接传输技术(如gRPC,HTTP)来拉取其他服务器上的分片冰进行合并,而不需要通过客户端中转

Plant B 分布式文件系统 HDFS Ceph

如果我们的数据量大, 可以考虑使用分布式文件系统来存储分片,这些系统天然支持分布式存储和数据合并,适合不同服务器上进行分片快速合并。

合并策略优化

合并优化

所有分片上传完成后,启动多个并行线程或者进程,在不同服务器上同事处理不同部分的合并任务。

部分合并与最终合并

先将一些较小的分片在服务器上合并成中间结果,再将中间结果传输到指定服务器进行最终合并。这种犯法可以减少单一服务器的负载

java 复制代码
@RestController
public class FileUploadController {
    @PostMapping("/upload_chunk")
    public ResponseEntity<?> uploadChunk(@RequestParam("file") MultipartFile file,
                                         @RequestParam("index") int index,
                                         @RequestParam("total") int total) {
        // 将分片保存到指定位置
        String filePath = saveChunk(file, index);
        // 保存分片元数据到数据库或缓存中
        saveMetadata(filePath, index, total);

        return ResponseEntity.ok().build();
    }

    @PostMapping("/merge_chunks")
    public ResponseEntity<?> mergeChunks(@RequestParam("fileName") String fileName) {
        // 获取所有分片的元数据
        List<String> chunkPaths = getChunkPaths(fileName);
        // 并行合并分片
        mergeChunksIntoFile(chunkPaths, fileName);
        return ResponseEntity.ok().build();
    }

    private String saveChunk(MultipartFile file, int index) {
        // 将分片保存到本地或分布式文件系统
        // 返回保存路径
    }

    private void mergeChunksIntoFile(List<String> chunkPaths, String fileName) {
        // 实现分片合并逻辑
    }
}
相关推荐
caoerzhong8 分钟前
JeeWMS 开源仓库管理系统权限与域验证解析:Java WMS 如何把数据权限管到仓、货主与人
java·python·开源·vue
君顾111 分钟前
西安 24 小时自助健身房系统开发实战指南
java·开发语言·健身房
用户0942485680311 分钟前
第9章:OpenJDK堆分代与 Serial/Parallel GC 基础
java·jvm
Wang's Blog20 分钟前
Java框架快速入门: Spring Security+OAuth2之RBAC与角色分层实践
java·网络·spring
caoerzhong32 分钟前
JeeWMS 开源仓库管理系统多租户架构解析:一套 Java WMS 如何同时服务多个货主与多个仓库
java·架构·开源·vue
sunshine22 girl38 分钟前
Java学习四 面向对象原理
java
干到60岁退休的码农40 分钟前
15.创建获取用户接口与JWT认证器
java·spring boot·mybatis
小羊没烦恼!1 小时前
Memory 记忆设计讨论:Agent Memory 的数据模型可以怎么设计
java·开发语言·前端·c++·c#
SL_staff1 小时前
战略落地的最后一公里:从目标到个人日历的自动化链路实现
java·github·全栈
用户3721574261351 小时前
Java 设置 Excel 数据验证:整数、日期、文本长度、下拉列表和时间
java