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

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

一、前端实现步骤

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('服务器内部错误');  
});

三、总结

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

相关推荐
胡西风_foxww13 分钟前
【ES6复习笔记】数值扩展(16)
前端·笔记·es6·扩展·数值
mosen86814 分钟前
uniapp中uni.scss如何引入页面内或生效
前端·uni-app·scss
白云~️15 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
沙尘暴炒饭17 分钟前
uniapp 前端解决精度丢失的问题 (后端返回分布式id)
前端·uni-app
昙鱼31 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
田猿笔记36 分钟前
解决 Node.js 单线程限制的有效方法
node.js
天天进步201537 分钟前
Vue项目重构实践:如何构建可维护的企业级应用
前端·vue.js·重构
蟾宫曲39 分钟前
Node.js 工具:在 Windows 11 中配置 Node.js 的详细步骤
windows·npm·node.js·前端工具
小华同学ai40 分钟前
vue-office:Star 4.2k,款支持多种Office文件预览的Vue组件库,一站式Office文件预览方案,真心不错
前端·javascript·vue.js·开源·github·office
APP 肖提莫41 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法