Send blob data to node using fetch, multer, express - javascript

Trying to send a blob object to my node server. On the client side I'm recording some audio using MediaRecorder and then I want to send the file to my server for processing.
saveButton.onclick = function(e, audio) {
var blobData = localStorage.getItem('recording');
console.log(blobData);
var fd = new FormData();
fd.append('upl', blobData, 'blobby.raw');
fetch('/api/test',
{
method: 'post',
body: fd
})
.then(function(response) {
console.log('done');
return response;
})
.catch(function(err){
console.log(err);
});
}
This is my express route, which uses multer:
var upload = multer({ dest: __dirname + '/../public/uploads/' });
var type = upload.single('upl');
app.post('/api/test', type, function (req, res) {
console.log(req.body);
console.log(req.file);
// do stuff with file
});
But my logs return nothing:
{ upl: '' }
undefined
Been spending a long time on this so any help appreciated!

I was just able to run a minimum configuration of your above example and it worked fine for me.
Server:
var express = require('express');
var multer = require('multer');
var app = express();
app.use(express.static('public')); // for serving the HTML file
var upload = multer({ dest: __dirname + '/public/uploads/' });
var type = upload.single('upl');
app.post('/api/test', type, function (req, res) {
console.log(req.body);
console.log(req.file);
// do stuff with file
});
app.listen(3000);
HTML file in public:
<script>
var myBlob = new Blob(["This is my blob content"], {type : "text/plain"});
console.log(myBlob);
// here unnecessary - just for testing if it can be read from local storage
localStorage.myfile = myBlob;
var fd = new FormData();
fd.append('upl', localStorage.myfile, 'blobby.txt');
fetch('/api/test',
{
method: 'post',
body: fd
});
</script>
The console.log(myBlob); on the frontend is printing Blob {size: 23, type: "text/plain"}. The backend is printing:
{}
{ fieldname: 'upl',
originalname: 'blobby.txt',
encoding: '7bit',
mimetype: 'text/plain',
destination: '/var/www/test/public/uploads/',
filename: 'dc56f94d7ae90853021ab7d2931ad636',
path: '/var/www/test/public/uploads/dc56f94d7ae90853021ab7d2931ad636',
size: 23 }
Maybe also try it with a hard-coded Blob like in this example for debugging purposes.

You don't need to use multer. Just use express.raw() inside app.post()
var express = require('express');
var app = express();
app.use(express.static('public')); // for serving the HTML file
app.post('/api/test', express.raw({type: "*/*"}), function (req, res) {
console.log(req.body);
// do stuff with file
});
app.listen(3000);

Related

How to pass a file from the client-side to the backend using Vue and Express?

I'm using Vue with NodeJs, Vuetify and Express. I load user's file with the Vuetify's component:
<v-file-input
v-model="documentFile.value"
:error-messages="documentFile.errors"
accept="application/pdf"
/>
Then I want to pass the file (that is stored in this.documentFile.value) to my backend, so it will upload it to the Drive. I pass the data using Vue Recourse:
var params = {
"data": this.data.value
"score": this.score.value
//"document_file": this.documentFile.value,
"comments": this.comments.value
};
Vue.http.put('http://localhost:8081/api/new-document', {params: params}).then(
response => {
console.log("Sent data");
}, response => {
console.error(response);
}
);
In my NodeJS backend I have:
router.put('/new-document', function(request, response) {
console.log("New Document");
console.log(request.query);
// Upload file to drive
const oauth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URI
);
response.status(200).send({});
});
How can I pass the file from the client to the backend?
EDIT: If I uncomment document_file, and try to print request.query, it prints:
{
data: { age: '27', name: 'robert' },
comments: 'comment',
"score": 89
}
For some reason, it ignores the document_file.
The code in my server.js:
const cors = require("cors");
const express = require("express");
const bodyParser = require("body-parser");
const routes = require('./routes');
const path = __dirname + '/../public/';
console.log("STARTED");
const app = express();
app.use(express.static(path));
var corsOptions = {
origin: "http://localhost:8080"
};
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// map requests to routes
app.use('/api', routes);
// set port, listen for requests
const PORT = process.env.PORT || 8081;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
To pass the file from the client to the backend, use the new FormData() object.
In your case you can do something like:
var params = new FormData();
params.append('data', this.data.value);
params.append('score', this.score.value);
params.append('document_file', this.documentFile.value)
params.append('comments', this.comments.value);
Then pass the object either using axios or Vue Recourse as you would like.
axios.put('url', params)
EDIT
You can use multer to upload your files either locally or to the cloud. In your case, you can upload to GoogleStorage
const multer = require('multer')
// You can start off by testing local uploads
const upload = multer({ dest: 'uploads/' })
// then use this to upload to Google Storage
const multerGoogleStorage = require('multer-google-storage')
const uploadHandler = multer({
storage: multerGoogleStorage.storageEngine({
autoRetry: true,
bucket: '<your_storage_bucket_name>',
projectId: '<your_project_ID>',
keyFilename: '<your_path_to_key_file>',
filename: (req, file, cb) => {
cb(null, `/<some_prefix_of_choice>/${Date.now()}_${file.originalname}`)
}
})
})
Upload the file either to local or Google Cloud
// local uploads (destination projectRoot/uploads)
router.put('/new-document', upload.single('document_file'), async (request, response) => {});
// or GoogleStorage
router.put('/new-document', uploadHandler.single('document_file'), async (request, response) => {});
Multiple files can also be uploaded
app.put('/new-document', upload.array('document_files', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
The document file(s) can then be accessible on the Express server using
request.file
You can the upload the file. The other form objects can also be accessible through request.body e.g.
request.body.data

NodeJS: cannot grab post request text field

I am new to Node JS, so things are not coming easy to me. The scenario is I have input field which will accept multiple files.
<input id="upload-input" type="file" name="uploads[]" multiple="multiple">
in my JS script I grab the the change event of this field, and create a post request to my uploader app which is running in different port using formData and ajax post method
$('#upload-input').on('change', function() {
var files = $(this).get(0).files;
if (files.length > 0) {
var formData = new FormData();
formData.append('directory', "path/to/directory");
for (var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('uploads[]', file, file.name);
}
$.ajax({
url: 'https://myurl.com:3000/upload',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(data) {
console.log(data);
},
});
}
});
Now the file is sending and in my backend I can upload that using formidable, but the problem is I cannot get the directory value, Here is my code
require('dotenv').load();
var express = require('express');
var app = express();
var path = require('path');
var formidable = require('formidable');
var fs = require('fs');
var session = require('express-session');
app.set('views', __dirname + '/public');
app.use('/uploads', express.static(process.env.USER_UPLOADS))
var cors=require('cors');
app.use(cors({origin:true,credentials: true}));
app.post('/upload', function(req, res) {
var user_folder = "path/to/directory/";
var form = new formidable.IncomingForm();
form.multiples = true;
form.uploadDir = path.join(__dirname, process.env.USER_UPLOADS + user_folder);
form.on('file', function(field, file) { fs.rename(file.path, path.join(form.uploadDir, file.name)); });
form.on('error', function(err) { console.log('An error has occured: \n' + err); });
form.on('end', function() { res.end('success'); });
form.parse(req);
});
var server = app.listen(3000, function(){
console.log('Server listening on port 3000');
});
I tried
console.log(req.body)
but it returns undefined, So how can I get the directory value from my backend?
Thanks in advance.
To fix your issue, I made some changes to your main app's server file
i.e. server.js/app.js/index.js anyone that applies to you. See changes below:
require('dotenv').load();
var express = require('express');
var app = express();
var path = require('path');
var bodyParser = require('body-parser');
var formidable = require('formidable');
var fs = require('fs');
var session = require('express-session');
var cors=require('cors');
app.set('views', __dirname + '/public');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors({ origin:true, credentials: true }));
app.use('/uploads', express.static(process.env.USER_UPLOADS));
app.post('/upload', function(req, res) {
var user_folder = "path/to/directory/";
var form = new formidable.IncomingForm();
form.multiples = true;
form.uploadDir = path.join(__dirname, process.env.USER_UPLOADS + user_folder);
form.on('file', function(field, file) { fs.rename(file.path, path.join(form.uploadDir, file.name)); });
form.on('error', function(err) { console.log('An error has occured: \n' + err); });
form.on('end', function() { res.end('success'); });
// Note the changes here
form.parse(req, (error, fields, files) => {
console.log(JSON.stringify({
fields, // { directory: "path/to/directory" }
files // contains the uploaded files
}), null, 2);
});
});
var server = app.listen(3000, function(){
console.log('Server listening on port 3000');
});
According to the docs at here, form.parse can take an optional callback function.
Parses an incoming node.js request containing form data. If cb is provided, all fields and files are collected and passed to the callback:
form.parse(req, function(err, fields, files) {
// ...
});

How to save file using Express 4 and Multer?

I'm trying to save incoming file from FormData xhr request but i can't even parse incoming request. This is how i'm trying to send file:
...
let xhr = new XMLHttpRequest(),
formData = new FormData();
for(let i = 0; i < this.files.length; i++) {
formData.append(this.files[i], this.files[i].name);
}
xhr.open('POST', URL, true);
xhr.send(formData);
...
And this is how i'm trying to catch it:
var express = require('express');
var router = express.Router();
var multer = require('multer');
var uploads = multer({dest: './uploads/'});
router.post('/upload', uploads.any(), function (req, res) {
console.log(req.files); // []
console.log(req.file); // undefined
console.log(req.body); //{ '[object File]': '20160715_104330.jpg' }
});
The image comes on the server but is not writing to the directory uploads.
How I can save the image in the directory uploads?
I found the error. The following is the corrected code line:
formData.append( 'Choose your Fieldname', this.files[i], this.files[i].name);
I just needed to put the first parameter is a string representing the fieldname.
See the result in the backend:
var express = require('express');
var router = express.Router();
var multer = require('multer');
var uploads = multer({dest: './uploads/'});
router.post('/upload', uploads.any(), function (req, res) {
console.log(req.files);
/* [ { fieldname: 'Choose your Fieldname',
originalname: '20160715_104330.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
destination: './uploads/',
filename: '72b7a52101537ab1006f4feb0fa752be',
path: 'uploads\\72b7a52101537ab1006f4feb0fa752be',
size: 233509 } ]
*/
console.log(req.file); // undefined
console.log(req.body); //{}
});
Below is a smaller code in the frontend:
...
upload(){
let xhr = new XMLHttpRequest(),
formData = new FormData();
formData.append('Choose your Fieldname', this.files[0], this.files[0].name);
xhr.open('POST', URL, true);
xhr.send(formData);
}
...

download don't provide client download file [duplicate]

How can I download a file that is in my server to my machine accessing a page in a nodeJS server?
I'm using the ExpressJS and I've been trying this:
app.get('/download', function(req, res){
var file = fs.readFileSync(__dirname + '/upload-folder/dramaticpenguin.MOV', 'binary');
res.setHeader('Content-Length', file.length);
res.write(file, 'binary');
res.end();
});
But I can't get the file name and the file type ( or extension ). Can anyone help me with that?
Update
Express has a helper for this to make life easier.
app.get('/download', function(req, res){
const file = `${__dirname}/upload-folder/dramaticpenguin.MOV`;
res.download(file); // Set disposition and send it.
});
Old Answer
As far as your browser is concerned, the file's name is just 'download', so you need to give it more info by using another HTTP header.
res.setHeader('Content-disposition', 'attachment; filename=dramaticpenguin.MOV');
You may also want to send a mime-type such as this:
res.setHeader('Content-type', 'video/quicktime');
If you want something more in-depth, here ya go.
var path = require('path');
var mime = require('mime');
var fs = require('fs');
app.get('/download', function(req, res){
var file = __dirname + '/upload-folder/dramaticpenguin.MOV';
var filename = path.basename(file);
var mimetype = mime.lookup(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
var filestream = fs.createReadStream(file);
filestream.pipe(res);
});
You can set the header value to whatever you like. In this case, I am using a mime-type library - node-mime, to check what the mime-type of the file is.
Another important thing to note here is that I have changed your code to use a readStream. This is a much better way to do things because using any method with 'Sync' in the name is frowned upon because node is meant to be asynchronous.
Use res.download()
It transfers the file at path as an “attachment”. For instance:
var express = require('express');
var router = express.Router();
// ...
router.get('/:id/download', function (req, res, next) {
var filePath = "/my/file/path/..."; // Or format the path using the `id` rest param
var fileName = "report.pdf"; // The default name the browser will use
res.download(filePath, fileName);
});
Read more about res.download()
For static files like pdfs, Word docs, etc. just use Express's static function in your config:
// Express config
var app = express().configure(function () {
this.use('/public', express.static('public')); // <-- This right here
});
And then just put all your files inside that 'public' folder, for example:
/public/docs/my_word_doc.docx
And then a regular old link will allow the user to download it:
My Word Doc
Here's how I do it:
create file
send file to client
remove file
Code:
let fs = require('fs');
let path = require('path');
let myController = (req, res) => {
let filename = 'myFile.ext';
let absPath = path.join(__dirname, '/my_files/', filename);
let relPath = path.join('./my_files', filename); // path relative to server root
fs.writeFile(relPath, 'File content', (err) => {
if (err) {
console.log(err);
}
res.download(absPath, (err) => {
if (err) {
console.log(err);
}
fs.unlink(relPath, (err) => {
if (err) {
console.log(err);
}
console.log('FILE [' + filename + '] REMOVED!');
});
});
});
};
In Express 4.x, there is an attachment() method to Response:
res.attachment();
// Content-Disposition: attachment
res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
'use strict';
var express = require('express');
var fs = require('fs');
var compress = require('compression');
var bodyParser = require('body-parser');
var app = express();
app.set('port', 9999);
app.use(bodyParser.json({ limit: '1mb' }));
app.use(compress());
app.use(function (req, res, next) {
req.setTimeout(3600000)
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept,' + Object.keys(req.headers).join());
if (req.method === 'OPTIONS') {
res.write(':)');
res.end();
} else next();
});
function readApp(req,res) {
var file = req.originalUrl == "/read-android" ? "Android.apk" : "Ios.ipa",
filePath = "/home/sony/Documents/docs/";
fs.exists(filePath, function(exists){
if (exists) {
res.writeHead(200, {
"Content-Type": "application/octet-stream",
"Content-Disposition" : "attachment; filename=" + file});
fs.createReadStream(filePath + file).pipe(res);
} else {
res.writeHead(400, {"Content-Type": "text/plain"});
res.end("ERROR File does NOT Exists.ipa");
}
});
}
app.get('/read-android', function(req, res) {
var u = {"originalUrl":req.originalUrl};
readApp(u,res)
});
app.get('/read-ios', function(req, res) {
var u = {"originalUrl":req.originalUrl};
readApp(u,res)
});
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + server.address().port);
});
you can use res.sendFile()... the Sample-download.xlsx should be in the same directory as this function.
const downloadFile = (req,res) => {
var options = {
root: path.join(__dirname),
};
let fileName = "Sample-download.xlsx";
res.sendFile(fileName, options, function (err) {
if (err) {
console.log(err);
return res.status(500).json({ success: false, message: "internal server error. please try again later" });
} else {
console.log("Sent:", fileName, "at", new Date().toString());
}
});
}

Download a file from NodeJS Server using Express

How can I download a file that is in my server to my machine accessing a page in a nodeJS server?
I'm using the ExpressJS and I've been trying this:
app.get('/download', function(req, res){
var file = fs.readFileSync(__dirname + '/upload-folder/dramaticpenguin.MOV', 'binary');
res.setHeader('Content-Length', file.length);
res.write(file, 'binary');
res.end();
});
But I can't get the file name and the file type ( or extension ). Can anyone help me with that?
Update
Express has a helper for this to make life easier.
app.get('/download', function(req, res){
const file = `${__dirname}/upload-folder/dramaticpenguin.MOV`;
res.download(file); // Set disposition and send it.
});
Old Answer
As far as your browser is concerned, the file's name is just 'download', so you need to give it more info by using another HTTP header.
res.setHeader('Content-disposition', 'attachment; filename=dramaticpenguin.MOV');
You may also want to send a mime-type such as this:
res.setHeader('Content-type', 'video/quicktime');
If you want something more in-depth, here ya go.
var path = require('path');
var mime = require('mime');
var fs = require('fs');
app.get('/download', function(req, res){
var file = __dirname + '/upload-folder/dramaticpenguin.MOV';
var filename = path.basename(file);
var mimetype = mime.lookup(file);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
var filestream = fs.createReadStream(file);
filestream.pipe(res);
});
You can set the header value to whatever you like. In this case, I am using a mime-type library - node-mime, to check what the mime-type of the file is.
Another important thing to note here is that I have changed your code to use a readStream. This is a much better way to do things because using any method with 'Sync' in the name is frowned upon because node is meant to be asynchronous.
Use res.download()
It transfers the file at path as an “attachment”. For instance:
var express = require('express');
var router = express.Router();
// ...
router.get('/:id/download', function (req, res, next) {
var filePath = "/my/file/path/..."; // Or format the path using the `id` rest param
var fileName = "report.pdf"; // The default name the browser will use
res.download(filePath, fileName);
});
Read more about res.download()
For static files like pdfs, Word docs, etc. just use Express's static function in your config:
// Express config
var app = express().configure(function () {
this.use('/public', express.static('public')); // <-- This right here
});
And then just put all your files inside that 'public' folder, for example:
/public/docs/my_word_doc.docx
And then a regular old link will allow the user to download it:
My Word Doc
Here's how I do it:
create file
send file to client
remove file
Code:
let fs = require('fs');
let path = require('path');
let myController = (req, res) => {
let filename = 'myFile.ext';
let absPath = path.join(__dirname, '/my_files/', filename);
let relPath = path.join('./my_files', filename); // path relative to server root
fs.writeFile(relPath, 'File content', (err) => {
if (err) {
console.log(err);
}
res.download(absPath, (err) => {
if (err) {
console.log(err);
}
fs.unlink(relPath, (err) => {
if (err) {
console.log(err);
}
console.log('FILE [' + filename + '] REMOVED!');
});
});
});
};
In Express 4.x, there is an attachment() method to Response:
res.attachment();
// Content-Disposition: attachment
res.attachment('path/to/logo.png');
// Content-Disposition: attachment; filename="logo.png"
// Content-Type: image/png
'use strict';
var express = require('express');
var fs = require('fs');
var compress = require('compression');
var bodyParser = require('body-parser');
var app = express();
app.set('port', 9999);
app.use(bodyParser.json({ limit: '1mb' }));
app.use(compress());
app.use(function (req, res, next) {
req.setTimeout(3600000)
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept,' + Object.keys(req.headers).join());
if (req.method === 'OPTIONS') {
res.write(':)');
res.end();
} else next();
});
function readApp(req,res) {
var file = req.originalUrl == "/read-android" ? "Android.apk" : "Ios.ipa",
filePath = "/home/sony/Documents/docs/";
fs.exists(filePath, function(exists){
if (exists) {
res.writeHead(200, {
"Content-Type": "application/octet-stream",
"Content-Disposition" : "attachment; filename=" + file});
fs.createReadStream(filePath + file).pipe(res);
} else {
res.writeHead(400, {"Content-Type": "text/plain"});
res.end("ERROR File does NOT Exists.ipa");
}
});
}
app.get('/read-android', function(req, res) {
var u = {"originalUrl":req.originalUrl};
readApp(u,res)
});
app.get('/read-ios', function(req, res) {
var u = {"originalUrl":req.originalUrl};
readApp(u,res)
});
var server = app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + server.address().port);
});
you can use res.sendFile()... the Sample-download.xlsx should be in the same directory as this function.
const downloadFile = (req,res) => {
var options = {
root: path.join(__dirname),
};
let fileName = "Sample-download.xlsx";
res.sendFile(fileName, options, function (err) {
if (err) {
console.log(err);
return res.status(500).json({ success: false, message: "internal server error. please try again later" });
} else {
console.log("Sent:", fileName, "at", new Date().toString());
}
});
}

Categories

Resources