multer: how to pass variables to next handler - javascript

I'm using multer to upload images. The following is my multer configuration:
import multer from "multer";
import * as mime from "mime-types";
import path from "path";
export const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req: any, file, cb) {
const name = path.parse(file.originalname).name + "_" + Date.now();
const extension = mime.extension(file.mimetype);
const filename = `${name}.${extension}`;
/**
req.fileInfo = {
filename,
name,
extension
} */
cb(null, filename);
},
});
export const upload = multer({ storage: storage });
I want to get fileInfo object in next handler.
import { upload } from "src/middlewares/imageUpload";
router.post('/imageUpload', upload.single("upload"), async (req, res) => {
//
const filename = req.fileInfo.filename;
});
According to this answer, res.local is the correct place to store variables between middleware and handlers.
But multer diskStorage configuration does not accept res parameter. I tried to store fileInfo object in req but sometimes it works and sometimes it doesn't(req.fileInfo is undefined for some routes, although the code is exactly the same).
How can I pass fileInfo to next handler?

multer middleware automatically sets the property named file on req object which contains the information about the uploaded file and can be accessed in the next middleware, so you don't need to set it your self.
If you still want to do that, you can attach fileInfo object to req object but it will only be available in next handlers where multer middleware is one of the middlewares in the pipeline. So req.fileInfo will be undefined for routes for which request doesn't goes through multer middleware.
You can make every request pass through the multer middleware but if that request doesn't contains any file that should be uploaded, req.fileInfo will be undefined because no file was uploaded by multer.

Related

Send image generated in frontend javascript

I got this third party lib which generates a screenshot.
I want to save this on my server. I'm using Axios.It's probably something with blobs, arraybuffers etc?
How do I send it?
Axios.post('/api/saveimage', { ??? })
Using NodeJs express on backend. How do I save this to a physical image file?
Well at the frontend you need to send it like this:
let formData = new FormData()
formData.append("image", file)
axios.post("/api/saveimage",formData)
At the first step you create a FormData, then you append the file. In this case i named the file image. Now lets go to the next step. You will need multer on your nodejs side.
npm i multer
The first think you need to do, is to create an middleware:
const multer = require("multer");
const whitelist = ["image/png", "image/jpeg", "image/jpg", "image/webp"];
const storeImages = multer.diskStorage({
destination: async function (req, file, cb) {
if (!whitelist.some((type) => type === file.mimetype)) {
return cb(new Error("File is not allowed"), "/");
}
cb(null, "your/destination/path");
},
filename(req, file, cb) {
let [name] = file.originalname.split(".");
cb(null, name + ".png");
},
});
exports.uploadImageStorage = multer({
storage: storeImages,
});
Here watch out: Your destination path should exist. Also dont forget an extension for your file in this case .png
Now you create your route:
const { uploadImageStorage } = require("../yourstorage")
app.post("/api/saveimage", uploadImageStorage.single("image"), (req, res) => {
let file = req.file
let path = file.path
})
Here you need to know at uploadImageStorage.single("image") i used image like i used it in formData.append("image", file) they need to be the same.
Now you can save the path of your file into a database. You can also transform your image with sharp if you want
From my experience if you have folder called static and you have a image inside of it like photo.png you usually get the photo with localhost:3000/photo.png and not with localhost:3000/static/photo.png
You will need to remove static from your path if you have this setup. Otherwise if you try to display the image on the frontend you wont see it.

Multer give undefined for req.file

I'm make api to upload files using node js.
i using multer for handle multipart/form-data
but when i console.log(req.file) it appears undefined
Route
const uploadController = require('../controller/upload.server.controller');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
/**
* Routes
*/
module.exports = function (app) {
app
.route('/api/upload')
.post(upload.single('image'), uploadController.upload);
};
Controller
exports.upload = async function (req, res) {
console.log(req.file);
};
my request with postman
Try using req.file as per the docs source: multer
// init
const multer = require('multer');
const path = require('path');
// "images" are the folder name
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'images');
},
});
// put this to config the multer
app.use(multer({ storage: fileStorage }).single('image'));
// this is for serving file staticly with "path" module
app.use('/images', express.static(path.join(__dirname, 'images')));
Also make sure you write enctype=multipart/form-data in the form tag not in the input tag AND
add type=file attribute in the input tag
Try this:
app.post('/api/upload', upload.single('image'), function (req, res, next) {
console.log(req.file);
})
I have also encountered similar problem. Everytime I restart my server postman provided req.file gives undefined. I thought maybe I habe made a bug but I couldn't figure out what was wrong.
Then I tried creating a new request in postman and when I requested req.file works perfectly. I am trying to figure out what is wrong with Postman or am I making a mistake while sending a request.

Write a file to the filesystem then download in Express

Is it possible to use res.download() after writing a file to the filesystem?
router.get('/exportjson', (req, res, next) => {
let json = `{"#dope":[{"set":"","val":"200"}],"comment":"comment","folderType":"window"}`
const file = `${__dirname}/upload-folder/export.JSON`;
fs.writeFile('file', json, 'application/json', function(){
res.download(file);
})
})
I'm not sure I fully understand your question, but I'm assuming you want to be able to save that json data to the path /upload-folder/export.json and then allow the browser to download the file using res.download() at the path GET /exportjson.
You've got a couple of issues. First, fs.writeFile takes a file path as the first argument, and you are just passing the string file. With your code, the data would be written to the current directory as file. You probably want to use the path module and create a path to the file you want to write, like so:
const path = require('path');
const jsonFilePath = path.join(__dirname, '../upload-folder/export.json');
Assuming the code is at routes/index.js, this path would point to the root directory of the project to the file upload-folder/export.json.
The data you want to write is in your variable json, but you have it stored as a string. I would actually leave it as an object:
let json = {
"#dope": [
{
"set":"",
"val":"200"
}
],
"comment":"comment",
"folderType":"window"
};
And then call JSON.stringify on it when you pass it to fs.writeFile as the second argument. You will also need to pass in the utf-8 option as the third argument, not application/json:
fs.writeFile(jsonFilePath, JSON.stringify(json), 'utf-8', function(err) {
In the callback to fs.writeFile, you want to call res.download and pass it the path to the file that you just wrote to the filesystem, which is stored in jsonFilePath (you had this part right, I just changed the variable name):
res.download(jsonFilePath);
Here is the relevant portion of the router file that has code to get everything working correctly:
const fs = require('fs');
const path = require('path');
const jsonFilePath = path.join(__dirname, '../upload-folder/export.json');
router.get('/exportjson', (req, res, next) => {
let json = {
"#dope": [
{
"set":"",
"val":"200"
}
],
"comment":"comment",
"folderType":"window"
};
fs.writeFile(jsonFilePath, JSON.stringify(json), 'utf-8', function(err) {
if (err) return console.log(err);
res.download(jsonFilePath);
});
});
Assuming this file lives in /routes/index.js, the file would be saved at /upload-folder/export.json.
Here is a gif showing how it looks on my machine:

trying to use the preservePath method in the Node.js express Multer API

I'm trying to use multer to upload a file. There is a boolean preservePath method, but the the API is vague on how to implement. I need to extract the original absolute file path from the file to be uploaded for use in a workflow pipeline on a closed network.
My understanding is that the optional methods are setup in the variable assignment.
Here is what I think is the relevant storage definition:
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
console.log(file);
}
});
const upload = multer({
//*********************************
preservePath: true,
//*********************************
storage: storage,
}).single('myImage');
Also, any guidance/input on how to construct clearer questions and clarifications would also be helpful.

Node js receive file in HTTP request

I try to create a server, which can receive a file from an HTTP request.
I use Postman as user agent and I add a file to the request. This is the request:
POST /getfile HTTP/1.1
Host: localhost:3000
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Cache-Control: no-cache
Postman-Token: 9476dbcc-988d-c9bd-0f49-b5a3ceb95b85
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="test.xls"
Content-Type: application/vnd.ms-excel
------WebKitFormBoundary7MA4YWxkTrZu0gW--
But when the request reaches the server I can not find the file in it (I mean in the request).
I tried to receive it from the body part of the request, but it returned > {} <. I tried to figure out, how can I refer to the name of the file, but unfortunately I can not find any reference in the request header for the name of the file...
Can anybody help me to find out, what should I do?
As a follow up to my comment, you can use the multer module achieve the thing that you want:
https://www.npmjs.com/package/multer
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer();
app.post('/profile', upload.array(), function (req, res, next) {
// req.body contains the text fields
});
var app = require('express')();
var multer = require('multer');
var upload = multer();
app.post('/your_path', upload.array(), function (req, res, next) {
// req.files is array of uploaded files
// req.body will contain the text fields, if there were any
});
You need to parse the form data from the request. There are a few packages that solves this problem, notably formidable, busboy (or busboy-connect), parted and flow.
Here's a solution using formidable, it is my preferred solution for things like image uploads because it saves to disk.
If you just want to read the file, you can use one of the other packages above.
Install formidable
npm install formidable --save
Then, in your server, you will have to parse the data from the client:
// Somewhere at the start of your file
var IncomingForm = require('formidable').IncomingForm
// ...
// Then in your request handler
var form = new IncomingForm()
form.uploadDir = 'uploads'
form.parse(request, function(err, fields, files) {
if (err) {
console.log('some error', err)
} else if (!files.file) {
console.log('no file received')
} else {
var file = files.file
console.log('saved file to', file.path)
console.log('original name', file.name)
console.log('type', file.type)
console.log('size', file.size)
}
})
A few things to note:
formidable saves files with new names, you can use fs to rename or move them
you can set form.keepExtensions = true if you want saved files to keep their extensions

Categories