4

I'm having troubles with node 16 and ES6. I'm trying to make a upload file controller but i'm stuck with req.file.stream which is undefined I'm using multer to handle upload files.

The first issue was __dirname undefined that I was able to fix with path and New Url.

The error I got with pipeline

node:internal/process/promises:246
          triggerUncaughtException(err, true /* fromPromise */);
          ^

TypeError [ERR_INVALID_ARG_TYPE]: The "source" argument must be of type function or an instance of Stream, Iterable, or AsyncIterable. Received undefined

my userRoutes.js

import express from "express";
import { signin, signup, logout } from "../Controller/AuthController.js";
import {
  getUsers,
  getUser,
  updateUser,
  deleteUser,
  follow,
  unfollow,
} from "../Controller/UserController.js";
import { upload } from "../Controller/UploadController.js";
import multer from "multer";

const router = express.Router();
// Auth
router.post("/signin", signin);
router.post("/signup", signup);
router.post("/logout", logout);

// users
router.get("/", getUsers);
router.get("/:id", getUser);
router.patch("/:id", updateUser);
router.delete("/:id", deleteUser);
router.patch("/follow/:id", follow);
router.patch("/unfollow/:id", unfollow);

// upload
router.post("/upload", multer().single("file"), upload);
export default router;

And my UploadController.js


import fs from "fs";
import { promisify } from "util";
import stream from "stream";

const pipeline = promisify(stream.pipeline);
// const { uploadErrors } = require("../utils/errors.utils");
import path from "path";
const __dirname = path.dirname(new URL(import.meta.url).pathname);

export const upload = async (req, res) => {
  try {
    // console.log(req.file);
    console.log(__dirname);
    if (
      !req.file.mimetype == "image/jpg" ||
      !req.file.mimetype == "image/png" ||
      !req.file.mimetype == "image/jpeg"
    )
      throw Error("invalid file");

    if (req.file.size > 2818128) throw Error("max size");
  } catch (err) {
    const errors = uploadErrors(err);
    return res.status(201).json({ err });
  }
  const fileName = req.body.name + ".jpg";
  await pipeline(
    req.file.stream,
    fs.createWriteStream(
      `${__dirname}/../client/public/uploads/profil/${fileName}`
    )
  );

  try {
    await User.findByIdAndUpdate(
      req.body.userId,
      { $set: { picture: "./uploads/profil/" + fileName } },
      { new: true, upsert: true, setDefaultsOnInsert: true },
      (err, docs) => {
        if (!err) return res.send(docs);
        else return res.status(500).send({ message: err });
      }
    );
  } catch (err) {
    return res.status(500).send({ message: err });
  }
};
David
  • 129
  • 1
  • 4

2 Answers2

2

Multer gives you the file as a Buffer, not a Stream. req.file.stream is not valid property, but req.file.buffer is: https://github.com/expressjs/multer#file-information.

From the look of your code, you're trying to save the file on disk. You can use multer's DiskStorage for that. Create a storage instance and pass it to the multer instance as a configuration:

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, `${__dirname}/../client/public/uploads/profil/`);
  },
  filename: function (req, file, cb) {
    cb(null, req.body.name + '.jpg');
  },
});

const upload = multer({ storage });

router.post('/upload', upload.single('file'), upload);

Have a look at this free Request Parsing in Node.js Guide for working with file uploads in Node.js.

Maxim Orlov
  • 1,942
  • 15
  • 18
  • He didn't specify the version of Multer he's using but version 2.0.0-rc.1 does support streams and he should follow the documentation on that version. https://www.npmjs.com/package/multer/v/2.0.0-rc.2 – Jeremy Apr 08 '22 at 14:31
1

if you want to use req.file.stream, you will need to install this version of multer:

npm install --save multer@^2.0.0-rc.1

and your code will work perfectly, just change your req.file.mimetype to req.file.detectedMimeType !!

Abdel
  • 404
  • 2
  • 8