前端大文件上传以及以外情况的解决

现在大部分的应用都会有上传文件的功能,小的可以直接正常直接上传,但是如果遇到大型文件需要上传那会面临着很多问题比如网络中断等问题会造成上传失败,下面我将针对大文件上传出一个解决方案,有什么问题麻烦各位指出。

一、前端实现步骤

1.文件选择

用户通过文件选择控件选择一个或多个大文件。

javascript 复制代码
<input type="file" id="fileInput" multiple>

2.文件校验

检查文件的大小和类型是否符合要求

javascript 复制代码
const fileInput = document.getElementById('fileInput');  
fileInput.onchange = (e) => {  
    const files = e.target.files;  
    for (let file of files) {  
        if (file.size > MAX_FILE_SIZE) {  
            alert('文件过大,请选择较小的文件');  
            return;  
        }  
        if (!VALID_FILE_TYPES.includes(file.type)) {  
            alert('不支持的文件类型');  
            return;  
        }  
    }  
};  

const MAX_FILE_SIZE =20 * 1024 * 1024; // 20MB  
const VALID_FILE_TYPES = ['image/jpeg', 'image/png', 'application/pdf'];//可以自行添加文件类型

3.文件分片

使用Blob对象的slice方法将文件分割成多个小块(分片)。

javascript 复制代码
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB  

function chunkFile(file, chunkSize) {  
    const chunks = [];  
    for (let start = 0; start < file.size; start += chunkSize) {  
        const end = Math.min(start + chunkSize, file.size);  
        chunks.push(file.slice(start, end));  
    }  
    return chunks;  
}

4.生成文件哈希

为文件生成一个唯一的哈希值(如MD5),用于标识文件和验证文件的完整性。

javascript 复制代码
import SparkMD5 from 'spark-md5';  

function getFileHash(file) {  
    return new Promise((resolve, reject) => {  
        const chunkSize = 2048 * 1024; // 2MB  
        const chunks = Math.ceil(file.size / chunkSize);  
        let currentChunk = 0;  
        const spark = new SparkMD5.ArrayBuffer();  
        const fileReader = new FileReader();  

        fileReader.onload = (e) => {  
            const chunk = e.target.result;  
            spark.append(chunk);  
            currentChunk++;  

            if (currentChunk < chunks) {  
                loadNext();  
            } else {  
                resolve(spark.end());  
            }  
        };  

        fileReader.onerror = (e) => {  
            reject('Error reading file: ' + e.message);  
        };  

        function loadNext() {  
            const start = currentChunk * chunkSize;  
            const end = start + chunkSize >= file.size ? file.size : start + chunkSize;  
            fileReader.readAsArrayBuffer(file.slice(start, end));  
        }  

        loadNext();  
    });  
}

5上传分片

使用FormData对象构建上传请求,将分片逐个上传到服务器。可以使用并发请求来提高上传速度

javascript 复制代码
async function uploadChunks(chunks, fileHash, fileName) {  
    const promises = chunks.map((chunk, index) => {  
        const formData = new FormData();  
        formData.append('file', chunk);  
        formData.append('index', index);  
        formData.append('fileHash', fileHash);  
        formData.append('fileName', fileName);  

        return fetch('/upload', {  
            method: 'POST',  
            body: formData,  
        }).then(response => response.json());  
    });  

    return Promise.all(promises);  
}

6.合并分片

当所有分片上传完成后,通知服务器合并分片。

javascript 复制代码
async function mergeChunks(fileHash, fileName, totalChunks) {  
    const response = await fetch('/merge', {  
        method: 'POST',  
        headers: {  
            'Content-Type': 'application/json',  
        },  
        body: JSON.stringify({ fileHash, fileName, totalChunks }),  
    });  

    return response.json();  
}

7.错误处理

处理上传过程中的各种错误,如网络错误、文件校验错误等。

javascript 复制代码
async function uploadFile(file) {  
    try {  
        const fileHash = await getFileHash(file);  
        const chunks = chunkFile(file, CHUNK_SIZE);  
        const fileName = file.name;  
        const totalChunks = chunks.length;  

        const uploadResults = await uploadChunks(chunks, fileHash, fileName);  
        const mergeResult = await mergeChunks(fileHash, fileName, totalChunks);  

        if (mergeResult.success) {  
            alert('文件上传成功');  
        } else {  
            alert('文件合并失败:' + mergeResult.message);  
        }  
    } catch (error) {  
        alert('文件上传失败:' + error.message);  
    }  
}  

fileInput.onchange = (e) => {  
    const files = e.target.files;  
    for (let file of files) {  
        uploadFile(file);  
    }  
};

二、后端实现步骤

  1. 接收分片

    后端接收分片并存储到临时位置

javascript 复制代码
// 示例:Node.js + Express  
const express = require('express');  
const multer = require('multer');  
const fs = require('fs');  
const path = require('path');  

const app = express();  
const upload = multer({ dest: 'uploads/temp' });  

app.post('/upload', upload.single('file'), (req, res) => {  
    const { index, fileHash, fileName } = req.body;  
    const chunkPath = path.join(__dirname, 'uploads/temp', `${fileHash}-${index}`);  
    fs.renameSync(req.file.path, chunkPath);  
    res.status(200).send({ success: true });  
});

2.合并分片

后端根据文件哈希和分片索引合并分片。

javascript 复制代码
app.post('/merge', (req, res) => {  
    const { fileHash, fileName, totalChunks } = req.body;  
    const filePath = path.join(__dirname, 'uploads', fileName);  
    const writeStream = fs.createWriteStream(filePath);  

    for (let i = 0; i < totalChunks; i++) {  
        const chunkPath = path.join(__dirname, 'uploads/temp', `${fileHash}-${i}`);  
        const data = fs.readFileSync(chunkPath);  
        writeStream.write(data);  
        fs.unlinkSync(chunkPath); // 删除临时分片文件  
    }  

    writeStream.end();  
    res.status(200).send({ success: true });  
});

3.错误处理

处理接收和合并过程中的各种错误,如文件校验错误、存储错误等。

javascript 复制代码
// 示例:错误处理逻辑  
app.use((err, req, res, next) => {  
    console.error(err.stack);  
    res.status(500).send('服务器内部错误');  
});

三、总结

通过以上步骤和技术实现,你可以实现一个前端大文件上传的功能。前端负责文件选择、校验、分片、哈希生成、上传和错误处理;后端负责接收分片、存储和合并分片。不过还有一些别的特殊情况要处理比如取消上传,上传进度展示等

相关推荐
沉默璇年1 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727572 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
SoaringHeart2 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
会发光的猪。2 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客2 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记2 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安3 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼3 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo3 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式