如何使用大于 1mb 的 socket.io 和 nodejs 实现房间到房间的文件共享系统?

问题描述

我想实现 socket.io 房间到房间的文件共享系统,以便用户可以将图像发送到他们各自的房间,所有用户都可以看到它,我尝试使用 base64 编码方法将发送图像文件发送到特定房间但它只能发送大约 700kb 到 800kb 的文件

Is there any easier way of doing this and can support larger files above 1mb and it should be able to load images progressively?

I am using ejs template engine,nodejs,socket.io,javascript.

Console.log("please help me guys if you any idea about this,I tried many things but none of them are working and I have read the socket.io documentation but didn't get any clue about it

我也尝试过二进制流,但没有成功,请帮我提供一些代码示例

解决方法

您可能会发现客户端使用房间名称将文件上传到您的 http 服务器更容易,然后让您的 http 服务器通过 socket.io 向房间中的所有其他客户端发送消息,其中包含客户端可以使用http下载文件。 socket.io 不是流协议,它是基于数据包或消息的协议,因此要发送大数据,必须将其分解为消息,然后在客户端重新组装。这可以做到,但这只是额外的非标准工作,http 上传和下载已经知道怎么做。

以下是步骤:

  1. 客户端通过 http post 将文件上传到服务器,并将房间名称作为表单中的一个字段。
  2. 服务器接收上传的文件,为其分配一个唯一 ID 并将其存储在服务器磁盘上的临时位置。
  3. 当文件上传完成后,服务器通过 socket.io 通知房间中的所有其他客户端文件已上传并可供下载,并向他们发送包含 uniqueID 的下载 URL。
  4. 每个客户端使用他们收到的唯一 URL 通过 http 发送下载文件的请求。
  5. 服务器根据 http 请求将文件提供给每个客户端。
  6. 服务器要么跟踪所有客户端现在是否已完成下载,要么在一段时间后根据文件的时间戳(仅清理磁盘空间)使用定期计时器上的一些常规清理功能删除文件。

您可以创建一个处理所有下载的路由:

const downloadRoot = "/temp/filexfer";

app.get("/download/:id",(req,res) => {
    const fullPath = path.resolve(path.join(downloadRoot,req.params.id));
    // detect any leading . or any double .. that might jump outside
    // the downloadRoot and get to other parts of the server
    if (!fullPath.startsWith(downloadRoot)) {
         console.log(`Unsafe download request ${fullPath}`);
         res.sendStatus(500);
         return;
    } 
    res.download(fullPath);
});

清理算法可能如下所示:

const fsp = require('fs').promises;
const path = require('path');
const oneHour = 1000 * 60 * 60;

// run cleanup once per hour
let cleanupTimer = setInterval(async () => {
    let oneHourOld = Date.now() - oneHour;
    try {
        let files = await fsp.readdir(downloadRoot,{withFileTypes: true});
        for (let f of files) {
             if (f.isFile()) {
                 let fullName = path.join(downloadRoot,f.name);
                 let info = await fsp.stat(fullName);
                 // if file modification time is older than one hour,remove it
                 if (info.mtimeMs <= oneHourOld) {
                     fsp.unlink(fullName).catch(err => {
                         // log error,but continue
                         console.log(`Can't remove temp download file ${fullName}`,err);
                     });
                 }
             }
        }
    } catch(e) {
        console.log(e);
    }
    
},oneHour);

// unref the timer so it doesn't stop node.js from exiting naturally
cleanupTimer.unref();
,

有很多方法可以做这种事情,这在很大程度上取决于您想要支持的架构类型。

通过 socket.io 或任何其他 web-socket 发送大文件没问题。它确实需要在您的网络应用程序上进行大量切割和重新组装,但它会起作用。

WebRTC 是另一种共享任何描述文件的方式,它不会给您的服务器带来任何负担,这很好。 (这里有一个教程https://ably.com/tutorials/web-rtc-file-transfer

这两种方法的问题在于它们是临时共享,房间的新用户将无法获得图像,除非您的服务器再次重新传输数据。

我的建议是直接将文件上传到 s3,然后共享一个可以在每个客户端上解析的链接。这将减轻服务器负担并减少您在后端服务器中的存储需求