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

POST请求后无法删除被Read Stream占用的文件夹,求解决方案

解决Read Stream占用导致文件夹无法删除的问题

这种文件被流占用删不掉的情况我之前也碰到过,大概率是流的资源没有完全释放,或者销毁时机不对。结合你给出的代码和描述,咱们一步步来排查解决:

1. 别用全局变量管理流,改用请求上下文或映射表

全局变量file很容易出问题——如果多个用户同时请求视频流,后面的请求会覆盖前面的流引用,导致旧的流根本没被正确销毁。建议用Map来存储每个请求对应的流,比如:

// 全局维护一个流映射表,用请求的文件夹名称作为key
const activeStreams = new Map();

app.get('/api/video/:folder', function(req, res) {
  const folder = req.params.folder; // 你代码里写的streamer应该是笔误,改成folder更合理
  const path = `D:/Clips/${folder}/clip.mp4`;
  try {
    const stat = fs.statSync(path);
    const fileSize = stat.size;
    const range = req.headers.range;

    // 处理range请求(这里补全你没写完的逻辑)
    let start = 0;
    let end = fileSize - 1;
    if (range) {
      const parts = range.replace(/bytes=/, "").split("-");
      start = parseInt(parts[0], 10);
      end = parts[1] ? parseInt(parts[1], 10) : end;
    }
    const chunksize = (end - start) + 1;

    // 创建读取流
    const fileStream = fs.createReadStream(path, { start, end });
    // 把流存入映射表
    activeStreams.set(folder, fileStream);

    // 监听流的close事件,自动从映射表移除
    fileStream.on('close', () => {
      activeStreams.delete(folder);
    });

    // 监听error事件,避免流出错后残留引用
    fileStream.on('error', (err) => {
      console.error('Stream error:', err);
      activeStreams.delete(folder);
      res.status(500).send('Stream error');
    });

    // 设置响应头并pipe流到响应
    res.writeHead(206, {
      'Content-Range': `bytes ${start}-${end}/${fileSize}`,
      'Accept-Ranges': 'bytes',
      'Content-Length': chunksize,
      'Content-Type': 'video/mp4',
    });
    fileStream.pipe(res);
  } catch (err) {
    console.error('Video request error:', err);
    res.status(404).send('Video not found');
  }
});

2. 销毁流后,必须等待close事件触发再删除文件夹

调用destroy()和unpipe()后,流不会立刻释放文件句柄,得等close事件确认资源完全释放后,再执行删除操作。比如你的POST请求处理逻辑可以改成这样:

app.post('/api/delete-folder', async function(req, res) {
  const folderToDelete = req.body.folder;
  const targetStream = activeStreams.get(folderToDelete);

  if (targetStream) {
    // 先销毁流并取消pipe
    targetStream.unpipe();
    targetStream.destroy();
    // 等待close事件触发,确保资源彻底释放
    await new Promise((resolve) => {
      targetStream.once('close', resolve);
    });
  }

  // 现在尝试删除文件夹
  try {
    await fs.promises.rm(`D:/Clips/${folderToDelete}`, { recursive: true, force: true });
    res.send('Folder deleted successfully');
  } catch (err) {
    console.error('Delete folder error:', err);
    res.status(500).send('Failed to delete folder');
  }
});

3. 检查是否有未处理的流错误

如果流在读取过程中出现错误(比如文件突然损坏),但你没监听error事件,流会处于挂起状态,文件句柄也不会释放。所以一定要给每个流加上error事件监听,及时清理资源。

4. 确保响应对象的状态

有时候即使你销毁了流,响应对象res如果没正确结束,也可能导致资源泄漏。可以在销毁流后手动结束响应:

targetStream.destroy();
res.end();

按照上面的方法调整后,应该就能解决文件被占用无法删除的问题了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:34:31