Azure blob storage createBlockBlobFromLocalFile() has Error : OutOfRangeInput - node.js

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');
});

Related

send data from client (reactjs) to server (nodejs) but empty

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);
};

Upload Text file from React to Node using multer

React Code for file upload:
onChangeHandler=event=>{
this.setState({
selectedFile: event.target.files[0],
loaded: 0,
})
console.log(event.target.files[0])
}
onClickHandler = () => {
const data = new FormData()
data.append('file', this.state.selectedFile)
console.log(this.state.selectedFile)
axios.post(`${config.server}/upload`, data,{})
.then(res => { // then print response status
console.log(res.statusText)
});
}
<div>
<input type="file" name="file" onChange={this.onChangeHandler}/>
<button type="button" class="btn btn-success btn-block" onClick={this.onClickHandler}>Upload</button>
</div>
File Handling code of Node:
var multer=require("multer");
var cors = require('cors');
app.use(cors())
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
var upload = multer({ storage: storage }).single('file')
app.post('/upload',function(req, res) {
console.log(req.file)
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
The onChangehandler is showing the file in react on console.log() but on submitting the data using the button Upload which uses the onClickHandler function. The response I get from the server is 500 and the directory with the name public where it should store the file is not getting created. What is going wrong if someone can help me. I am new to file handling in react and node. I am using the following link as reference. It is not necessary to store the file. All I need to do is process the file and read the contents inside the file. If there is some other way of doing instead of storing it.
Thanks for your help.
The problem has been solved.
I didn't have the folder public which is my destination for saving the file. There is no changes needed in the code. The destination folder needs to be present. Multer cannot create the folder if not present.

Parse form value with formidable to filename

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.

Read file to bytes in Node using Express

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);
});
});

file upload error in node.js

To upload a file in node.js express I am using multer module. The code itself in a separate file is working like charm. but if I place the same code in my project(html code in one file and routing in another file) its not working.
Html code:
<form method="post" action="uploadgallerypic" enctype="multipart/form-data" >
<input type="file" name="gallerypic" />
<input type="submit" value="upload" />
</form>
corresponding routes.js code
app.use(multer({ dest: './uploads'}));
app.post('/uploadgallerypic', function(req, res) {
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
console.log("Error in writing pic to disk");
});
}
});
});
After clicking on submit the very first statement console.log('New photo added') which prints to console is not executing. Browser simply rotates and finally says 'No Data received'. But If I create a single file with these two blocks then its working fine.
var express = require('express');
var multer = require('multer');
var app = express();
var form = "<form method=\"post\" action=\"uploadgallerypic\" enctype=\"multipart/form-data\" >" +
"<input type=\"file\" name=\"gallerypic\" />" +
"<input type=\"submit\" value=\"upload\" />" +
"</form>";
app.use(multer({dest:'./uploads/'}));
app.get('/', function (req, res){
res.writeHead(200, {'Content-Type': 'text/html' });
res.end(form);
});
var fs = require('fs');
app.post('/uploadgallerypic', function(req, res) {
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
if(err) {
console.log("Error in writing pic to disk");
}
});
}
});
res.redirect('/profile');
});
app.listen(8080);
please tell me what I am missing here.
Edit#1
I removed app.post('/uploadgallerypic', function(req, res) block from routes.ejs to see the error "Cannot POST /uploadgallerypic" but I am not getting such error, browser simply rotating and says no data received. If I remove the enctype='multipart/form-data' satement from html code then I am getting the exact error "Cannot POST /uploadgallerypic". Is there any problem in using enctype='multipart/form-data'.
Please help me.
I think this might be minor typos...there are at least three things in the 'broken' example that are wrong/different from your 'working' example.
1) Is the dest value good?
In your broken example, change this line:
app.use(multer({ dest: './uploads'}));
...to this:
app.use(multer({ dest: './uploads/'}));
(Note the addition of the trailing slash on the dest path. Maybe not important, but I didn't go read the multer source to see if it matters. I just know it matters in some other situations, such as with some situations with grunt.)
2) Your app.post looks off:
Change this:
app.post('/uploadgallerypic', function(err, res) {
...to this?
app.post('/uploadgallerypic', function(req, res) {
(was there a reason you had err in there?)
3) And related to #2...
From this:
fs.readFile(req.files.gallerypic.path, function(req, data) {
...to this:
fs.readFile(req.files.gallerypic.path, function(err, data) {
(So...did you copy the code around or rewrite it by hand and accidentally swap which argument goes where?)
don't directly inject multer into express.
like this way
app.use(multer({ dest: './uploads/'}));
instead
1) create object of multer
var Upload = multer({
storage: storage
}).any('gallerypic');
2) create storage for gallerypic (upload file )
// storage for upload file.
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './file');
},
filename: function (req, file, callback) {
callback(null, file.originalname);
}
});
3) define your route
router.post('/uploadgallerypic', postData);
4) Invoke multer in your route.
function postData(req,res){
Upload(req, res, function (error) {
if (error) {
res.status(400).send(error);
}
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
if(err) {
console.log("Error in writing pic to disk");
}
});
}
});
res.redirect('/profile');
})
}
refer this like : How to return id of uploaded file (upload with multer in nodejs express app)

Resources