On a web-application that I am working on, users can upload a PDF file. I would like to read this PDF file to a buffer of bytes, which I can pass on to my database to save it as a BLOB. I am doing this in Node, using the Express framework.
Currently, I have the following:
Upload form:
<form id='uploadForm' method='post' enctype='multipart/form-data'>
<div class='form-group'>
<div class='form-inline'>
<label for='file'>File:</label>
<input type='file' name='file'>
</div>
</div>
<!--- some other fields --->
</form>
Server side:
router.post('/', function(req, res) {
var file = req.files.file;
var path = file.path;
var fsiz = file.size;
var buffer = new Buffer(fsiz);
fs.read(file, buffer, 0, fsiz, 0, function (err, bytesRead, buffer) {
console.log(err);
console.log(bytesRead);
console.log(buffer);
});
});
This gives me a Bad argument error on the fs.read function call. What is wrong and how can I fix it?
fs.read()'s first argument is supposed to be a file descriptor object, as returned by fs.open().
You can either first call fs.open(), or else use fs.readFile(), which takes a path:
router.post('/', function(req, res) {
var file = req.files.file;
var path = file.path;
var fsiz = file.size;
var buffer = new Buffer(fsiz);
fs.open(path, 'r', function(err, fd) {
fs.read(fd, buffer, 0, fsiz, 0, function (err, bytesRead, buffer) {
console.log(err);
console.log(bytesRead);
console.log(buffer);
});
});
});
router.post('/', function(req, res) {
var file = req.files.file;
var path = file.path;
var fsiz = file.size;
var buffer = new Buffer(fsiz);
fs.readFile(path, function (err, data) {
console.log(err);
console.log(data);
});
});
Related
hi guys i try to upload file from react-nodejs to google clode storage ,
in client when i upload file and console log the file , it show the file array but when i try to send to server side , the array is empty
this is client
const [myFile, setmyFile] = useState("");
const onFileUpload = () => {
console.log(myFile);
Axios.post("http://localhost:10000/uploads", { myFile: myFile });
};
<div>
<h1>GeeksforGeeks</h1>
<h3>File Upload using React!</h3>
<div>
<input
type="file"
onChange={(event) => {
setmyFile(event.target.files[0]);
}}
/>
<button onClick={onFileUpload}>Upload!</button>
</div>
this is server
app.post("/uploads", async (req, res, next) => {
try {
const myFile = req.body.myFile;
console.log(myFile);
const imageUrl = await uploadImage(myFile);
res.status(200).json({
message: "Upload was successful",
data: imageUrl,
});
} catch (error) {
next(error);
}
});
can someone help me , why "myFile" return "{}"
Simple way of file uploading with react to node sever is.
On React here is how you want to handle things using axios
const data = new FormData();
data.append('media_file', file_input) // Note the file in quotes is the key that the server will use to retrive the input i.e **file_input** in this case
axios.post(url, data).then(res=>{
console.log(res)
}).catch(error=>{
console.log(error)
})
So now how you handle this on your nodejs is like this I will be using formidable as bodyParser very easy to use
const Formidable = require("formidable"); //Meant for body parsing
router.post('/api/file-upload', (req, res)=>{
const form = new Formidable.InconmingForm();
form.parse(req, (error, fields, files)=>{
const {media_file} = files
//Destructing 'media_file' remember name that we stated on the client
// it was 'media_file' now that is what I want to de-structure within files which comes
//with formidable
})
})
So now if you log media_file you will see all that you need about file then you can continue with your logic of uploading to google cloud
In client side you have to add your file to formData object, and set the Content-Type header to multipart/form-data.
Client side code -
const onFileUpload = () => {
console.log(myFile);
try {
const formData = new FormData()
formData.append('file', myFile)
Axios.post("http://localhost:10000/uploads", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
} catch (error) {
console.error('Error while uploading image to server', error)
}
};
<div>
<h1> GeeksforGeeks </h1> <h3 > File Upload using React! </h3>
<div>
<input
type="file"
onChange={
(event) => {
setmyFile(event.target.files[0]);
}
}
/>
<button onClick={onFileUpload}> Upload! </button>
</div>
</div>
Server side:
You have to use multer or some other npm package to upload the files in the server side.
Once image is uploaded to google cloud storage, delete the file from local disk. finally block in below code is deleting the file from local disk once image is uploaded successfully or if there is any error in uploading the image.
const multer = require('multer')
const fs = require('fs')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname)
}
})
const upload = multer({ storage: storage }).single('file')
app.post("/uploads", async (req, res, next) => {
upload(req, res, async function (error) {
if (error instanceof multer.MulterError) {
return res.status(500).json(error)
} else if (error) {
return res.status(500).json(error)
}
const { filename } = req.file
var fileStream = fs.createReadStream(req.file.path)
try {
const options = {
filename
}
const imageUrl = await uploadImage(fileStream, options)
res.status(200).json({
message: "Upload was successful",
data: imageUrl,
});
} catch (error) {
next(error);
} finally {
fs.unlink(req.file.path, function (error) {
if (error) {
console.log('Error on deleting file from the path: ', req.file.path)
}
console.log('File deleted successfully from the disk')
})
}
})
});
As #Akanksha singh mentioned, you need to set the Content-Type header to multipart/form-data and use the formData object on the client side:
const onFileUpload = () => {
console.log(myFile);
try {
const formData = new FormData()
formData.append('file', myFile)
Axios.post("http://localhost:10000/uploads", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
} catch (error) {
console.error('Error while uploading image to server', error)
}
};
<div>
<h1> GeeksforGeeks </h1> <h3 > File Upload using React! </h3>
<div>
<input
type="file"
onChange={
(event) => {
setmyFile(event.target.files[0]);
}
}
/>
<button onClick={onFileUpload}> Upload! </button>
</div>
</div>
After testing this, logging the req.body on the server side returns a buffer instead of an empty object.
In order to deal with this buffer, which contains the form data, I suggest you follow the steps mentioned in the official docs to deal with multipart/form-data in Cloud Functions
Here is the code sample from the docs:
/**
* Parses a 'multipart/form-data' upload request
*
* #param {Object} req Cloud Function request context.
* #param {Object} res Cloud Function response context.
*/
const path = require('path');
const os = require('os');
const fs = require('fs');
// Node.js doesn't have a built-in multipart/form-data parsing library.
// Instead, we can use the 'busboy' library from NPM to parse these requests.
const Busboy = require('busboy');
exports.uploadFile = (req, res) => {
if (req.method !== 'POST') {
// Return a "method not allowed" error
return res.status(405).end();
}
const busboy = new Busboy({headers: req.headers});
const tmpdir = os.tmpdir();
// This object will accumulate all the fields, keyed by their name
const fields = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads = {};
// This code will process each non-file field in the form.
busboy.on('field', (fieldname, val) => {
/**
* TODO(developer): Process submitted field values here
*/
console.log(`Processed field ${fieldname}: ${val}.`);
fields[fieldname] = val;
});
const fileWrites = [];
// This code will process each file uploaded.
busboy.on('file', (fieldname, file, filename) => {
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[fieldname] = filepath;
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
// File was processed by Busboy; wait for it to be written.
// Note: GCF may not persist saved files across invocations.
// Persistent files must be kept in other locations
// (such as Cloud Storage buckets).
const promise = new Promise((resolve, reject) => {
file.on('end', () => {
writeStream.end();
});
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
fileWrites.push(promise);
});
// Triggered once all uploaded files are processed by Busboy.
// We still need to wait for the disk writes (saves) to complete.
busboy.on('finish', async () => {
await Promise.all(fileWrites);
/**
* TODO(developer): Process saved files here
*/
for (const file in uploads) {
fs.unlinkSync(uploads[file]);
}
res.send();
});
busboy.end(req.rawBody);
};
Hi Im using node and formidable to submit a file in a form, this file's URL I need to save in a global variable to later use it with WATSON IBM image recognition api.
I'm new with node so I'm stuck, the variable name is newpath, I'm able to print it after the form is submitted, but can't access the variable later.
I must be doing something wrong, I really appreciate if you can point me out my mistake.
const http = require('http');
var formidable = require('formidable');
const hostname = '127.0.0.1';
const port = 3500;
var fs = require('fs');
/// WATSON
var VisualRecognitionV3 = require('watson-developer-cloud/visual-recognition/v3');
var visualRecognition = new VisualRecognitionV3({
version: '2018-03-19',
iam_apikey: 'xxxxxxx'
});
// SERVER AND FORM
const server = http.createServer((req, res) => {
if (req.url == '/fileupload') {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var oldpath = files.filetoupload.path;
var newpath = '/users/myuser/coding/visualr/' + files.filetoupload.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
// this is the path, variable newpath, but can't be accessed
// outside this function, tried to make it global but didn't work either
res.write('newpath');
res.end();
});
});
} else {
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>');
return res.end();
}
});
var images_file = fs.createReadStream(newpath);
// I want to put the variable newpath in this function: but it doesn't work...
var params = {
images_file: images_file,
};
visualRecognition.classify(params, function(err, response) {
if (err)
console.log(err);
else
console.log(JSON.stringify(response, null, 2))
});
// ENDS
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
The variable is defined within the context of the if (req.url == '/fileupload') {...} block, so it won't be available outside of that block.
To use the variable everywhere in the code, define it outside of the createServer block:
var newpath; // this variable will have global context for this file
const server = http.createServer((req, res) => {
if (req.url == '/fileupload') {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var oldpath = files.filetoupload.path;
// set the variable to its intended value here
newpath = '/users/myuser/coding/visualr/' + files.filetoupload.name;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write('File uploaded and moved!');
res.write('newpath');
res.end();
});
});
} else {
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>');
return res.end();
}
});
console.log(newpath); // the variable should be available here, because of its context
I'm uploading an image using multipart/form-data, i want to resize it before storing it anywhere on the disk. I'm using gm to accomplish this but was not able to do it.
<form id="uploadForm" enctype="multipart/form-data" method="post" action="/upload">
<input type="file" name="userFile" />
<input type="submit" value="Upload File" name="submit">
</form>
here is the js file, now i want to resize the image without storing it anywhere on the disk using Imagemagick(gm) module in node. I'm new to node, how can we use the part and resize the image.
var express = require('express');
var multiparty = require("multiparty");
var app = express();
const sharp = require('sharp');
const gm = require('gm').subClass({imageMagick: true});
app.get('/', function(req, res){
res.sendFile('index.html' , { root : __dirname});
});
app.post('/upload', function(req, res){
console.log("in upload")
var count = 0;
var form = new multiparty.Form();
// Errors may be emitted
// Note that if you are listening to 'part' events, the same error may be
// emitted from the `form` and the `part`.
form.on('error', function(err) {
console.log('Error parsing form: ' + err.stack);
});
// Parts are emitted when parsing the form
form.on('part', function(part) {
// You *must* act on the part by reading it
// NOTE: if you want to ignore it, just call "part.resume()"
if (!part.filename) {
// filename is not defined when this is a field and not a file
console.log('got field named dd' + part.name);
// ignore field's content
part.resume();
}
if (part.filename) {
// filename is defined when this is a file
count++;
console.log('got file named ' + part.name);
// console.log(part);
part.on('data', (chunk) => {
console.log("chunck: "+chunk);
var readStream = fs.createReadStream(chunk);
gm(readStream, part.filename)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm');
if (!err) console.log('resize done');
else
console.log('gm error: '+err);
});
});
// ignore file's content here
part.resume();
}
part.on('error', function (err) {
// decide what to do
});
});
// Close emitted after form parsed
form.on('close', function() {
console.log('Upload completed!');
res.setHeader('text/plain');
res.end('Received ' + count + ' files');
});
// Parse req
form.parse(req);
});
provide originalPath and thumbnailPath thumbnail in this case your
resize image
function resizeImage(originalPath, thumbnailPath, callback) {
const gm = require('gm').subClass({imageMagick: true});
gm(originalPath)
.resize(WIDTH, HEIGHT, "!")
.autoOrient()
.write(thumbnailPath, (err, data) => {
callback(err)
})
}
You're trying to read the uploaded file stream instead of passing it to imageMagick. Also, you're using resume() on the received file, discarding it. Try changing this:
if (part.filename) {
// filename is defined when this is a file
count++
console.log('got file named ' + part.name)
// console.log(part);
part.on('data', (chunk) => {
console.log('chunck: ' + chunk)
var readStream = fs.createReadStream(chunk)
gm(readStream, part.filename)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm')
if (!err) console.log('resize done')
else
console.log('gm error: ' + err)
})
})
// ignore file's content here
part.resume()
}
For this:
if (part.filename) {
// filename is defined when this is a file
count++
console.log('got file named ' + part.name)
// console.log(part);
gm(part)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm')
if (!err) console.log('resize done')
else
console.log('gm error: ' + err)
})
// ignore file's content here; but we don't want that!
// part.resume()
}
So I have this code to upload an image to azure blob storage.
var blobSvc = azure.createBlobService(config.BLOB_ACCOUNT, config.BLOB_KEY);
controllers.upload = function (req, res, next){
req.pipe(req.busboy);
var fstream;
req.busboy.on('file', function (fieldname, file, filename) {
fstream = fs.createWriteStream(__dirname + '/upload/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
blobSvc.createBlockBlobFromLocalFile('resources',
filename,
fstream.path,
function(error, result, response) {
if (error) {
res.send(error);
return;
}
res.send(result);
});
});
});
}
The path was /var/www/html/foo-project/api/controllers/upload/ragnar.jpg, the account name is all small letters no other characters so is the container name.
And the image sure is present on that path.
But im still getting this error.
{code: "OutOfRangeInput", statusCode: 400, requestId: "00d83e3f-0001-002f-2c40-8cf7a5000000"}
How do I fix this? Thanks for the help.
EDIT:
The full response :
There is an example using express framework for uploading blob via http request on Azure offical GitHub repository, please see the source code line 97 at https://github.com/Azure/azure-storage-node/blob/master/examples/blobuploader/server.js#L97.
Update
For example, the HTML form as below.
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="text" name="title"><br>
<input type="file" name="upload" multiple="multiple"><br>
<input type="submit" value="Upload">
</form>
Here is the code sample using multiparty package (npm install multiparty).
var multiparty = require('multiparty');
app.post('/upload', function (req, res) {
var blobService = azure.createBlobService();
var form = new multiparty.Form();
form.on('part', function(part) {
if (part.filename) {
var size = part.byteCount - part.byteOffset;
var name = part.filename;
blobService.createBlockBlobFromStream('<container-name>', name, part, size, function(error) {
if (error) {
res.send({ 'Error': error });
}
});
} else {
form.handlePart(part);
}
});
form.parse(req);
res.send('OK');
});
I´m using formidable to handle my file uploads in NodeJs. I´m a little stuck at parsing field values.
How do I get the value of project_id to the form handler, so I can write the parameter in my filename?
<input type="text" id="project_id" value="{{projects._id}}" readonly>
EDIT
To be more specific, here´s a detailed view of my form-upload handling:
app.post('/uploads/', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
res.writeHead(200, {'content-type': 'image/jpeg'});
res.write('received upload: \n\n');
var project = fields.project_id;
res.end(util.inspect(project, {fields: fields, files: files}));
});
form.on('end', function(project, fields, files){
console.log(project);
/*Temporary location of our uploaded file */
var temp_path = this.openedFiles[0].path;
/*The file name of the uploaded file */
var file_name = project + '.' + this.openedFiles[0].name;
I can log the var project in the form.parse part. But I don´t get the variable in the form.on('end'... part.
HTML form
<form id="uploadForm"
enctype="multipart/form-data"
action="/uploads/"
method="post">
<input type="text" name="project_id" id="project_id" value="{{projects._id}}" readonly>
<input multiple="multiple" type="file" name="upload" />
<button type="submit">Upload</button>
</form>
Formidable's end callback doesn't take any parameters, but I'm not sure you even need to call it if you're using the parse callback. I think what you're looking for is something like this:
var fs = require('fs');
app.post('/uploads', function(req, res, next) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
if (err) next(err);
// TODO: make sure my_file and project_id exist
fs.rename(files.my_file.path, fields.project_id, function(err) {
if (err) next(err);
res.end();
});
});
});
You would need to listen for the end() event if you chose not to use the parse callback, like this:
new formidable.IncomingForm().parse(req)
.on('file', function(name, file) {
console.log('Got file:', name);
})
.on('field', function(name, field) {
console.log('Got a field:', name);
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
res.end();
});
Client side script:
//Upload the file
var fd = new FormData();
//Take the first selected file
fd.append("dbDocPath", 'invoices/' + file.name);
fd.append("file", file);
$http({
method: 'POST',
url: $rootScope.apiUrl + 'uploadDocToServer',
data: fd,
headers: {
'Content-Type': undefined
},
//prevents serializing payload. don't do it.
transformRequest: angular.identity,
}).success(function (response) {
if (response.success) {
}
})
Server side script:
var fileDir = path.join(__dirname, '/../uploads');
// create an incoming form object
var form = new formidable.IncomingForm();
var dbDocPath = '';
form.parse(req)
.on('field', function (name, field) {
//console.log('Got a field:', field);
//console.log('Got a field name:', name);
dbDocPath = field;
})
.on('file', function (name, file) {
//console.log('Got file:', name);
// specify that we want to allow the user to upload multiple files in a single request
//form.multiples = true;
// store all uploads in the /uploads directory
form.uploadDir = fileDir;
fs.rename(file.path, path.join(form.uploadDir, file.name));
// every time a file has been uploaded successfully,
// rename it to it's orignal name
var bucket = new AWS.S3();
//console.log(dbDocPath);
var params = {
Bucket: DocsConfig.bucketName,
Key: dbDocPath,
Body: fs.createReadStream(path.join(form.uploadDir, file.name)),
ACL: 'public-read'
};
bucket.putObject(params, function (perr, pres) {
if (perr) {
//console.log("Error uploading data: ", perr);
} else {
fs.unlinkSync(path.join(form.uploadDir, file.name));
//console.log("Successfully uploaded data", pres);
}
});
})
.on('error', function (err) {
res.send({'success': false, error: err});
})
.on('end', function () {
res.send({'success': true});
});
// parse the incoming request containing the form data
//form.parse(req);
Just keep one thing in mind that the sequence of sending parameters to formData() should be same as mentioned in above code as file upload needs path to upload to the destiny.