You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React前端接收NodeJS返回文件异常:文件变大无法打开

问题:前端下载的文件体积变大且无法打开

我在NodeJS应用中实现了一个API接口,通过接收前端传递的文件名,查找指定文件夹中的对应文件。后端代码如下:

const fileDirectory = 'C:/Sites/private-uploads';
try {
    // Extract filename from request parameters or query string
    const body = ctx.request.body; // Assuming filename is passed as a parameter in the route
    const filename = body.data.filename;

    // Check if the file exists
    const filePath = path.join(fileDirectory, filename);
    console.log(filePath);
    if (!fs.existsSync(filePath)) {
        ctx.status = 404;
        ctx.body = { error: 'File not found' };
        return;
    }

    // Read the file contents as raw binary data
    const fileContents = await fs.readFile(filePath, null); // Passing null as the encoding parameter
    const fileSizeKB = fileContents.length / 1024; // Convert bytes to kilobytes
    console.log('File size:', fileSizeKB.toFixed(2), 'KB');

    // Set appropriate headers
    ctx.set('Content-Type', 'application/octet-stream'); // Set Content-Type to indicate binary data
    ctx.set('Content-Disposition', `attachment; filename="${filename}"`); // Set filename for download

    // Send the file contents as the response body
    ctx.body = fileContents;
} catch (error) {
    console.error(error);
    ctx.status = 500;
    ctx.body = { error: 'Error retrieving file contents' };
}

后端打印的文件大小显示正常。我的React应用中接收文件的代码如下:

const getFile = async (filename) => {
        try {
            const response = await api.post('/api/get-file', {
                data: {
                    filename: filename
                }
            });
            if (response.data) {
                console.log(response.data);
                const blob = new Blob([response.data]);
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = filename;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                window.URL.revokeObjectURL(url);
            }
        } catch (error) {
            console.log(error);
        }
    }

但前端接收并生成的文件体积变大,且无法正常打开,请问哪里操作出错了?


问题原因及修复方案

核心原因

前端请求时未指定响应数据类型为二进制,导致请求库(默认是axios)自动将二进制数据解析为UTF-8字符串。字符串再转换为Blob时,会出现编码失真,最终造成文件损坏、体积膨胀。

1. 前端修复:指定响应类型为blob

修改请求配置,明确告诉axios返回二进制Blob对象,无需手动转换:

const getFile = async (filename) => {
    try {
        const response = await api.post('/api/get-file', {
            data: {
                filename: filename
            },
            responseType: 'blob' // 关键配置:直接接收Blob
        });
        if (response.data) {
            const url = window.URL.createObjectURL(response.data);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }
    } catch (error) {
        console.log(error);
    }
}

2. 后端可选优化:流式传输大文件

如果涉及大文件下载,fs.readFile会将整个文件加载到内存,影响性能。建议改用流式传输:

const filePath = path.join(fileDirectory, filename);
if (!fs.existsSync(filePath)) {
    ctx.status = 404;
    ctx.body = { error: 'File not found' };
    return;
}

// 设置响应头
ctx.set('Content-Type', 'application/octet-stream');
ctx.set('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`);

// 流式输出文件
ctx.body = fs.createReadStream(filePath);

3. 安全优化:避免文件名XSS风险

后端直接拼接文件名到响应头存在XSS风险,需对文件名进行转义:

ctx.set('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`);

内容的提问来源于stack exchange,提问作者Gery

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:55:27