node-formidable / formidable

The most used, flexible, fast and streaming parser for multipart form data. Supports uploading to serverless environments, AWS S3, Azure, GCP or the filesystem. Used in production.
MIT License
7k stars 680 forks source link

arguments about fileWriteStreamHandler #821

Closed Xiaoooyooo closed 1 year ago

Xiaoooyooo commented 2 years ago

I'm trying to do something before the file has been write to the directory. So I found that fileWriteStreamHandler may be helpful. And it did help me a lot. And I found that there is a problem with the type definition file. Here is the type:

/**
         * which by default writes to host machine file system every file parsed; The function should
         * return an instance of a Writable stream that will receive the uploaded file data. With this
         * option, you can have any custom behavior regarding where the uploaded file data will be
         * streamed for. If you are looking to write the file uploaded in other types of cloud storages
         * (AWS S3, Azure blob storage, Google cloud storage) or private file storage, this is the option
         * you're looking for. When this option is defined the default behavior of writing the file in the
         * host machine file system is lost.
         *
         * @default null
         */
        fileWriteStreamHandler?: (() => Writable) | undefined;

Note that the arguments of the fileWriteStreamHandler, there is nothing here, but there is something actually:

VolatileFile {
    _events: [Object: null prototype] { error: [Function (anonymous)] },
    _eventsCount: 1,
    _maxListeners: undefined,
    lastModifiedDate: null,
    filepath: 'D:\\My Code Library\\daily\\build\\data\\blogs\\Cookie.md',
    newFilename: 'Cookie.md',
    originalFilename: 'Cookie.md',
    mimetype: 'application/octet-stream',
    hashAlgorithm: false,
    createFileWriteStream: [Function: fileWriteStreamHandler],
    size: 0,
    _writeStream: null,
    hash: null,
    [Symbol(kCapture)]: false
  }

So I think this should be a bug about the definition file.

tunnckoCore commented 2 years ago

@Xiaoooyooo oh thanks! Sure, I think it should be (file: VolatileFile | PersistentFile), PR is welcome

KennanChan commented 1 year ago

Is it possible to pass in more params?

I upload my file and I want it to be streamed to another server via sftp. The final filename is determined by some other form fields that posted along with the uploaded file. I cannot create the writable with only the file itself.

GrosSacASac commented 1 year ago

@KennanChan

Use a variable that is closed over


let finalFilename;

 const form = formidable({
      fileWriteStreamHandler: (/* file */) => {
        // use variable finalFilename here
        return writable;
      },
    });

form.on('field', (name, value) => {
    if (name===`finalFilename`) {
        finalFilename = value
    }
});

Note this only works if finalFilename is sent before the file (inside the formdata)

IlirBajrami commented 1 year ago

Would you guys be kind a fix my code here so i can be able to upload my audio files to Firebase Storage.

const form = new formidable.IncomingForm();

      form.parse(req, async function (err, fields, files) {
        console.log("FILEEEE", files.mediaFile);
        const storageRef = ref(storage, "mediaa");
        await uploadBytes(storageRef, files.mediaFile).then((snapshot) => {
          console.log("Uploaded a blob or file!", snapshot);
        });
        res.send({ fields, files });
      });

Never used Formidable before so i don't really understand this. I was reading the docs that i should pass fileWriteStreamHandler to the options but i don't know how to write the handler function for this. Thanks!

Xiaoooyooo commented 1 year ago

@IlirBajrami Hi, I'm not that familiar with Firebase Storage, I read the doc for while, and here is my finally demo, maybe you can try this:

const http = require("http");
const fs = require("fs");
const formidable = require("formidable");
const stream = require("stream");

// there are some other type stream in nodejs, here I use Writable
class MyWritable extends stream.Writable {
  _write(chunk, encoding, callback) {
    console.log(encoding, chunk);
    // upload the buffer here
    setTimeout(() => {
      // remember to call `callback` when finished upload one chunk
      callback();
    }, 1000);
  }
  _final(callback) {
    console.log("_final");
    callback();
  }
}

const server = http.createServer((req, res) => {
  const { method, url } = req;
  console.log(method, url);
  if (url === "/") {
    res.writeHead(200, {
      "content-type": "text/html",
    });
    return fs.createReadStream("index.html").pipe(res);
  }
  if (method === "POST" && url === "/upload") {
    return formidable({
      fileWriteStreamHandler: () => {
        return new MyWritable();
      },
    }).parse(req, (err, fields, files) => {
      if (err) {
        res.statusCode = 500;
        return res.end(500);
      }
      res.writeHead(200, {
        "content-type": "application/json",
      });
      res.end(JSON.stringify({ files, fields }));
    });
  }

  res.statusCode = 404;
  res.end("404");
});

server.listen(8888, () => {
  console.log("http://127.0.0.1:8888\n");
});
tunnckoCore commented 1 year ago

@Xiaoooyooo might be right.

@IlirBajrami, I'm not familiar much with Firebase Storage either, but you can check out our example for S3 upload, it's should be something similar - https://github.com/node-formidable/formidable/blob/master/examples/store-files-on-s3.js

IlirBajrami commented 1 year ago

@Xiaoooyooo @tunnckoCore thank you for your answers. They both helped me fix my file uploading. I was using Nextjs so it was a bit different but i managed to make it work. :)

gboer commented 1 year ago

this is fixed in version 2.0.6 of @types/formidable, so this issue can be closed :)