Node JS, Formidable, Cloudinary Image Upload - javascript

I'm working on an application using Node JS, Formidable, and Cloudinary dependencies that lets users upload images to a Cloudinary database.
Users will upload images from a multipart form, and I want the fields to become variables to be passed to the Cloudinary upload function.
My code works but I'm having trouble passing form information as Cloudinary parameters.
Here is my current code, which passes the original file name:
router.post('/upload', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
form.on('end', function(fields, files) {
var temp_path = this.openedFiles[0].path;
var file_name = this.openedFiles[0].name;
cloudinary.uploader.upload(temp_path, function(result) { console.log(result) },
{ public_id: file_name });
})
But I don't want the original file name. Instead of var file_name I want var image_name which is provided by the user from the form

BTW, there's no need to handle end, since you're providing a callback to parse(). The callback is called when end is fired.
The text the user enters will be in fields.

Related

how to upload file from frontend and post it in a local folder using fs and express [duplicate]

This question already has an answer here:
How to upload, display and save images using node.js and express
(1 answer)
Closed 8 months ago.
I think I should use fs but I am not pretty sure how
ejs:
<form action="/products" method="POST">
<input type="file" name="image" id="image">
<button class="submit">Submit</button>
</form>
app.js
app.post('/products', (req, res) => {
console.log(req.body)
console.log(req.body.image) //console logs a string
fs.writeFile('./image.png', req.body.image, (err) => {
if (err) throw err;
console.log('saved!');
});
res.render('products', { title: 'bib' })
})
but it doesn't work since it's returning a string instead of a file and as I said I wanna save the file locally using fs
See The Form Element on MDN:
enctype
If the value of the method attribute is post, enctype is the MIME type
of the form submission. Possible values:
application/x-www-form-urlencoded: The default value.
multipart/form-data: Use this if the form contains elements with type=file.
You have not set the <form enctype="multipart/form-data"> so the browser will not upload the data.
Your server-side code is missing the part where you add the body parsing middleware, but if it is supporting the form you have then it is for URL Encoded Form Data and not for Multipart Form Data.
You need a body parser that can handle the data format you are using.
Express is packaged with body-parser which doesn't support multipart form bodies but suggests a number of options.
This does not handle multipart bodies, due to their complex and
typically large nature. For multipart bodies, you may be interested in
the following modules:
busboy and connect-busboy
multiparty and connect-multiparty
formidable
multer
Multer is probably the most popular of those.
You'll need something like:
app.post('/products', upload.single('image'), (req, res) => {
… where upload is provided by Multer and image is the name of the file input. You can then access the image via req.file.
You can try using multer which is for uploading files in express.
const multer = require('multer')
const upload = multer({
dest: 'path to where ever you want it to be saved locally in the server, eg ./images',
}) // the middleware
app.post('/products', upload.single('image'), (req, res) => {
console.log(req.body)
console.log(req.body.image) //console logs a string
fs.writeFile('./image.png', req.body.image, (err) => {
if (err) throw err;
console.log('saved!');
});
res.render('products', { title: 'bib' })
})

How to avoid a form being resubmitted in node.js / express / multer when there's no response?

I have a problem in that sometimes my form (submitted in a standard way via the Node.Js app) takes too long to be processed.
On the backend (app.js) the logic looks like this:
var multer = require('multer')
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './temp')
},
filename: function(req, file, cb) {
cb(null, file.originalname)
},
})
var upload = multer({
storage: storage
})
app.post(
'/import',
upload.array('uploadedFiles', 50),
imports.submit
)
So what happens then is that the POST request happens again (probably after not receiving any response) and the files get resubmitted. My log shows /POST --/--.
How can I avoid having that happen?
I tried to use the following code:
$('#submitform').on('submit', function(e){
e.preventDefault();
$(this).find('input[type=submit]').attr('disabled', 'disabled');
$('#message').html('Importing the data');
$("#submitform").unbind('submit').submit()
});
But it simply does not submit the form.
How could I fix this issue?
I can't use AJAX as the response I get from the node.js app as I'd have to rewrite too much backend.

Failing to open image file that has been saved as raw data, sent from client

Here are the steps my application is doing:
User uploads an image (.PNG) file in the browser.
Browser sends the raw image data to my server.
The server then saves the file in my file system.
The problem is that my file system complains that the file is not a PNG file when I try to open it.
I'm trying to localize where the problem occurs without success. When I look at the file data using VIM it looks the same to me, with the same number of lines and the file contents both start with:
<89>PNG^M
^Z
^#^#^#^MIHDR^#^#^BD^#^#^#Î^H^B^#^#^#P6<8a><9a>^#^#^#^CsBIT^H^H^HÛáOà^ ^#^#_zTXtRaw profile type APP1^#^#^H<99>ãJOÍK-ÊLV((ÊOËÌIåR^#^Cc^S. ^SK^SK£D^C^C^C^K^C^H04006^D<92>F#
...
However, the file sizes are different, with the file I'm writing from my server being larger. So obviously they are different in some way.
I tried doing diff file1 file2 in the command line and it just gives me binary files differ without showing me the difference... ?? Strange.
So I'm currently confused about how the files differ, and where in my code this difference gets introduced...
I feel I'm missing some crucial knowledge about how some things work here under the hood, and I would very much appreciate if someone smarter than me could help me out here.
Code
Client:
<input id="icon-button-file" type="file" onChange={handleChange} />
<label htmlFor="icon-button-file">
<Button/>
</label>
function handleChange(event: any) {
if (!event.target.files[0]) return
const file = event.target.files[0]
const reader = new FileReader()
reader.onload = function (e) {
const instance = axios.create({
baseURL: 'http://localhost:3000/',
timeout: 5000,
headers: { 'Content-Type': 'application/json' }
})
instance.post(
'image',
JSON.stringify({
imageData: e.target.result.toString()
})
).then(result => {
console.log(result)
})
}
reader.readAsBinaryString(file)
}
Server:
app.post(`http://localhost:3000/image`, (req, res) => {
fs.writeFile('img.png', req.body.imageData, (err) => {
console.log(err)
})
})
EDIT:
I made it work by sending the image content to the server as a dataUrl instead, using reader.readAsDataUrl(). This encodes the image data as a Base64 string, which seems like a common practice. However, I'm still curious why sending the raw data doesn't work.
You can use Formidable to handle files in nodejs easily. Good to hear that you got it running already. I hope this helps to someone else. this covers some basics
and yes another approach is to encode to Base64 and decode back to a file from the server side. cheers
var express = require('express');
var router = express.Router();
var fs = require('fs');
var formidable = require('formidable');
/* GET home page. */
router.get('/', function(req, res, next) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<form action="fileupload" method="post" enctype="multipart/form-data">');
res.write('<input type="file" name="filetoupload"><br>');
res.write('<input type="submit">');
res.write('</form>');
res.end()
});
router.post(`/fileupload`, (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var oldpath = files.filetoupload.path;
var newpath = './public/' + files.filetoupload.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.end();
});
});
})
module.exports = router;
Answering my own question.
The reason for the problem was that I was sending the image binary data over http which can corrupt the data.
See: https://stackoverflow.com/a/201510/6017605
Since base64 encodes it as text, it can safely be transmitted.
This also helped me understand the problem: https://www.jscape.com/blog/ftp-binary-and-ascii-transfer-types-and-the-case-of-corrupt-files

req.files does not exist while sending form data in nodejs

I am tying to store a form data along with files. when I am use form without this enctype="multipart/form-data" works fine but req.files dosent exist in req which i need to upload files. and when i use enctype in form, still req.files dosent exist and req.body does not have any data.
I was trying to implement multer to handle files but req.files dosent exist so didnt get any idea.
my route
const urlencodedParser = bodyParser.urlencoded({extended: true});
router.post('/save_file', urlencodedParser, home.add_file);
my controller
exports.add_file = function(req, res){
console.log(req.body);
console.log(req.files);
}
Any help will be appriciated.
bodyParser, the library you use to parse your request from the server, doesn't parse files, you need to use another library, (multer is very good and easy).
So first :
Install Multer: npm install multer --save
Here the link of Multer: https://github.com/expressjs/multer
Use this example as base:
let multer = require("multer"); //the library
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'path/to/upload/your/file');
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});//Configure the place you will upload your file
let upload = multer({ storage: storage }); //instanciation of multer
// image is the name of the input in the form
router.post('/your_endpoint', upload("image"), (req, res)=> {
let file = req.file; //contains the file
let path = file.path; //contains the paths
})
Try using multer in your node app -
https://www.npmjs.com/package/multerenter link description here

Multer and AngularJS

I can't figure out how to post through angular $http. Why? I'd like multer to parse and store my file and angular to get a copy when it is done. Better yet, I'd love to just let angular get a copy, then pass it down to the server.
I am able to upload a file using the snippets below:
// view (jade)
.container
form(action="/upload", method="POST", enctype="multipart/form-data")
input(type="file", name="f")
input(type="submit", value="Upload")
// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
console.log(req.file);
});
Running this with any sample file, returns the json I want to the console and the image is stored successfully in the proper directory. However:
// view (jade)
form(enctype="multipart/form-data" ng-submit="uploadFile(file)")
input(type="file", name="f", ng-model="file")
input(type="submit", value="Upload")
// ctrl (angular)
$scope.uploadFile = function(file) {
console.log(file);
$http.post('/upload', file);
};
// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
console.log(req.file);
});
This returns undefined on both console inputs, even though I didn't change the file, I only changed how it was sent.
This leads me two believe one of two things:
The data submitted is not what I think it is. If this is the case, what is it? Nothing prints on the log.
OR
The data submitted is altered in some way by angular. If this is the case, how? Again nothing prints on the log.
In the first case, by sending the data directly through the form to the server, you let html do the transformation magic under the hood.
In the second case, by posting through AngularJs via $http, you need to tell the middleware that is $http, the required transformation it needs to do to the request to match the attributes you passed to the form.
For having struggled with this myself for a while, here is an adaptation of the code I used (for $resource). But I think it should work for its underlying $http.
So in your controller:
$scope.uploadFile = function(file) {
console.log(file);
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined},
enctype: 'multipart/form-data'
}
})
.success(function(){
})
.error(function(){
});
};
For that to work, you need to bind the file input field to the model. Of course, Angularjs chooses not to do that natively for some reasons.
In the jade view:
form(enctype="multipart/form-data" ng-submit....)
input(type="file", name="f", ng-model="file" on change='angular.element(this).scope().readFile(this)')
input(type="submit", value="Upload")
onchange is JavaScript and not AngularJs and it triggers the scope.readfile() function that we need to define:
In the controller:
$scope.readfile = function(elem) {
var file= elem.files[0];
var reader = new FileReader();
reader.onload = function() {
$scope.$apply(function(){
$scope.file = file;
$scope.imageUrl = reader.result // to display image via ng-Src
})
}
reader.readAsDataURL(file);
}
I think that should bring back some html magic back into angular when working with forms.
I suggest you look into JavaScript's onchange, FormData, and FileReader for code snippets like these and better documentation.

Categories

Resources