@ludovicm67/lib-filetransfer
    Preparing search index...

    @ludovicm67/lib-filetransfer

    File transfer library

    NPM

    This library is helpful if you want to send huge files to other users.

    Homepage & live interactive demo: https://lib-filetransfer.ludovicm67.fr/

    Technical (API) documentation can be found here: https://lib-filetransfer.ludovicm67.fr/api/

    Source code is available on GitHub at: https://github.com/ludovicm67/lib-filetransfer

    Here are some of the supported features:

    • this library is written using TypeScript:
      • nice autocompletion
      • checking of types
    • split files into smaller parts that can be sent using the way you want
    • integrated retry mechanism
    • client can ask for a specific offset and limit
    • parallel requests

    Imagine you have two users: a sender and a receiver. The sender want to send a file to the receiver. Both of them will start by creating a file pool. The sender will store the file into his pool, and will get some metadata that will include a unique ID for this file. He will send the metadata to the other user using a communication channel of his choice. The receiver will store the received metadata into his pool, so that it is aware of the existence of the file. The receiver can display a message to the user asking if he wants to download the file or not. If he wants to download the file, a call should be performed to the pool to trigger the download. The library is not taking care about transmitting data to the other user, you will to take care about it yourself. You can use a WebRTC DataChannel or a WebSocket for example. That's why the library is asking for a callback function when you call the downloadFile method on the pool. You will need to send and receive data between both users, so that you can transmit requests and responses from both of them. If a part of the file was not received within a specified amount of time, it will retry a few time. If it is still failing after a specified amount of retries, it will throw an error. Once all the parts of the file are received by the receiver, the library will sort all parts, generate the file and store it as a Blob object. Starting now, you will be able to access the file, and open it in a new browser tab for the receiver user for example. You can also send a custom message to the sender saying that the file was fully received, … the only limit is now your imagination!

    Here is an illustrated flow between our two users:

    %%{init:{"theme":"dark"}}%% sequenceDiagram participant S as Sender participant R as Receiver S->>S: addFile() → metadata S->>R: send metadata (your channel) R->>R: storeFileMetadata() Note over R,S: downloadFile() asks for parts,<br/>several in parallel loop for each part R->>S: ask part (offset, limit) S->>R: send part data end Note over R: a part timed out?<br/>re-ask it (retry mechanism) R->>S: ask missing part again S->>R: send part data R->>R: all parts received → sort and build Blob R->>R: getFile()
    %%{init:{"theme":"default"}}%% sequenceDiagram participant S as Sender participant R as Receiver S->>S: addFile() → metadata S->>R: send metadata (your channel) R->>R: storeFileMetadata() Note over R,S: downloadFile() asks for parts,<br/>several in parallel loop for each part R->>S: ask part (offset, limit) S->>R: send part data end Note over R: a part timed out?<br/>re-ask it (retry mechanism) R->>S: ask missing part again S->>R: send part data R->>R: all parts received → sort and build Blob R->>R: getFile()
    sequenceDiagram
      participant S as Sender
      participant R as Receiver
      S->>S: addFile() → metadata
      S->>R: send metadata (your channel)
      R->>R: storeFileMetadata()
      Note over R,S: downloadFile() asks for parts,<br/>several in parallel
      loop for each part
        R->>S: ask part (offset, limit)
        S->>R: send part data
      end
      Note over R: a part timed out?<br/>re-ask it (retry mechanism)
      R->>S: ask missing part again
      S->>R: send part data
      R->>R: all parts received → sort and build Blob
      R->>R: getFile()

    To summarize, you will need to take care of having a communication channel between users, and the library is doing the rest.

    Add this library to your NodeJS project's dependencies:

    npm i @ludovicm67/lib-filetransfer
    

    And in your project, instantiate a pool like this:

    import { TransferFilePool } from "@ludovicm67/lib-filetransfer";

    const filePool = new TransferFilePool({ maxBufferSize: 5000 });

    To send a file to another user, you will need to add it to the pool like this:

    // you have a variable called `file` of type `File`:
    const fileName = file.name;
    const fileMetadata = await filePool.addFile(file, fileName);

    // you have a variable called `file` of type `Blob`:
    const fileName = "file-name.txt"; // specify a file name
    const fileMetadata = await filePool.addFile(file, fileName);

    You should find a way to send the content of the fileMetadata variable to the other user.

    Here is how you should add the metadata to the pool of the receiver:

    // here is how to import the type representing the file metadata, in case you need it:
    import { TransferFileMetadata } from "@ludovicm67/lib-filetransfer";

    // you have a variable called `fileMetadata` of type `TransferFileMetadata` containing the metadata received from the sender

    const fileId = filePool.storeFileMetadata(fileMetadata);

    The receiver has a new variable fileId containing the ID of the file.

    We will make the assumption that sendToOtherUser is a function that is taking data to send to the other user. You will need to take care of it by yourself.

    So here is how to trigger the download of the file:

    // this function will be called to ask each file part to the other user
    const callbackToAskFilePart = (
    fileId: string,
    offset: number,
    limit: number
    ) => {
    sendToOtherUser({
    type: "file-ask-part",
    fileId,
    offset,
    limit,
    });
    };

    // we wait that the full file is downloaded
    await filePool.downloadFile(fileId, callback);
    const file = filePool.getFile(fileId);
    const url = URL.createObjectURL(file.data);

    // open the downloaded file in a new browser tab
    window.open(url, "_blank").focus();

    On the sender side, we imagine you get the value of the sendToOtherUser function in the request variable:

    const { fileId, offset, limit } = request;

    // this will contain the part of the requested file
    const data = filePool.readFilePart(fileId, offset, limit);

    // send this data to the other user
    sendToOtherUser({
    type: "file-part",
    fileId,
    offset,
    limit,
    data,
    });

    On the receiver side, we imagine you get the value of the sendToOtherUser function in the response variable:

    const { fileId, offset, limit, data } = response;

    filePool.receiveFilePart(fileId, offset, limit, data);

    The sender is able to send all the parts of the file, and the receiver to ask and store them.

    You now have all the logic to build your application!

    The data variable we used in the previous section is an ArrayBuffer. In some cases, your communication channel may only allow the sending of strings.

    If you need to do some conversions, you can use the following utility functions:

    // convert ArrayBuffer to a string
    import { arrayBufferToString } from "@ludovicm67/lib-filetransfer";
    // `ab` variable is an ArrayBuffer
    const string = arrayBufferToString(ab);

    // convert string to an ArrayBuffer
    import { stringToArrayBuffer } from "@ludovicm67/lib-filetransfer";
    // `str` variable is a string
    const arrayBuffer = stringToArrayBuffer(str);

    This library is licensed under the MIT License.