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

NodeJS读取Excel文件后无法删除本地文件问题排查

问题分析与解决

我在S3 Bucket托管了文件,通过Axios请求获取Excel文件,随后将其写入本地文件系统。接着使用XLSX库将Excel文件数据转换为JSON并作为响应返回。返回响应后,我想要删除本地的该Excel文件。

尝试删除文件时,删除操作失败,甚至读取文件也失败;但如果本地已存在该文件,则能正常读取并返回响应。

原代码

const response = await axios.get(data.me_url, { responseType: 'arraybuffer' });

if (response) {

    const ostream = await fs.createWriteStream(`./${filename}`, 'binary');
    ostream.write(response.data);
    ostream.end();

    const file = xlsx.readFile(`./${filename}`);
    const sheetNames = file.SheetNames;
    const totalSheets = sheetNames.length;

    let parsedData = [];

    for (let i = 0; i < totalSheets; i++) {

        const tempData = xlsx.utils.sheet_to_json(file.Sheets[sheetNames[i]]);
        tempData.shift();
        parsedData.push(...tempData);
    }

    if (parsedData.length > 0) {
        console.log('if filename',filename);
        try {
            fs.unlinkSync(`./${filename}`);
          
            console.log("Delete File successfully.");
            return res.status(200).send({ 'data': parsedData, 'message': 'Success', 'code': 200 });
        } catch (error) {
            console.log(error);
        }
    }
}

错误原因

  • 核心问题:文件写入未完成就执行后续操作
    fs.createWriteStream是异步操作,调用ostream.end()后文件并不会立刻写入完成。此时直接执行xlsx.readFile会读取到不完整的文件(甚至文件还未创建成功),导致读取失败;后续删除操作也会因为文件处于占用状态或不存在而失败。
  • 额外问题:await fs.createWriteStream无效,因为createWriteStream返回的是流对象而非Promise,await无法等待写入完成。

解决方法

方法1:用Promise封装流的写入完成事件

将流写入过程包装为Promise,确保写入完成后再执行读取/删除:

const response = await axios.get(data.me_url, { responseType: 'arraybuffer' });

if (response) {
    // 封装流写入为Promise,等待写入完成
    await new Promise((resolve, reject) => {
        const ostream = fs.createWriteStream(`./${filename}`, 'binary');
        ostream.write(response.data);
        ostream.end();
        
        ostream.on('finish', resolve);
        ostream.on('error', reject);
    });

    const file = xlsx.readFile(`./${filename}`);
    const sheetNames = file.SheetNames;
    const totalSheets = sheetNames.length;

    let parsedData = [];

    for (let i = 0; i < totalSheets; i++) {
        const tempData = xlsx.utils.sheet_to_json(file.Sheets[sheetNames[i]]);
        tempData.shift();
        parsedData.push(...tempData);
    }

    if (parsedData.length > 0) {
        console.log('if filename', filename);
        try {
            fs.unlinkSync(`./${filename}`);
            console.log("Delete File successfully.");
            return res.status(200).send({ 'data': parsedData, 'message': 'Success', 'code': 200 });
        } catch (error) {
            console.log(error);
            return res.status(500).send({ 'message': '删除临时文件失败', 'code': 500 });
        }
    }
}

方法2:使用fs.promises.writeFile简化操作

既然已获取完整的arraybuffer,无需使用流,直接用异步写入方法更简洁:

const response = await axios.get(data.me_url, { responseType: 'arraybuffer' });

if (response) {
    // 异步写入文件,await确保写入完成
    await fs.promises.writeFile(`./${filename}`, response.data, 'binary');

    const file = xlsx.readFile(`./${filename}`);
    const sheetNames = file.SheetNames;
    const totalSheets = sheetNames.length;

    let parsedData = [];

    for (let i = 0; i < totalSheets; i++) {
        const tempData = xlsx.utils.sheet_to_json(file.Sheets[sheetNames[i]]);
        tempData.shift();
        parsedData.push(...tempData);
    }

    if (parsedData.length > 0) {
        console.log('if filename', filename);
        try {
            await fs.promises.unlink(`./${filename}`); // 改用异步删除,避免阻塞事件循环
            console.log("Delete File successfully.");
            return res.status(200).send({ 'data': parsedData, 'message': 'Success', 'code': 200 });
        } catch (error) {
            console.log(error);
            return res.status(500).send({ 'message': '删除临时文件失败', 'code': 500 });
        }
    }
}

额外优化建议

  • 尽量避免使用同步方法(如unlinkSync),HTTP请求处理中同步操作会阻塞事件循环,影响服务性能,优先用异步的fs.promises方法。
  • 给临时文件添加唯一标识(如UUID),避免多请求操作同名文件导致冲突。
  • 在xlsx.readFile外层添加try-catch,处理文件读取失败的情况,防止程序崩溃。

内容的提问来源于stack exchange,提问作者Digvijay Singh Thakur

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:40:33