I want to use a Node/Express server to stream a file to the client as an attachment. I would like to make an async request from the client to a /download endpoint and then serve an object received via API proxy to the client as a downloadable file (similar to the way res.attachment(filename); res.send(body); behaves).
For example:
fetch(new Request('/download'))
.then(() => console.log('download complete'))
app.get('/download', (req, res, next) => {
// Request to external API
request(config, (error, response, body) => {
const jsonToSend = JSON.parse(body);
res.download(jsonToSend, 'filename.json');
})
});
This will not work because res.download() only accepts a path to a file. I want to send the response from an object in memory. How is this possible with existing Node/Express APIs?
Setting the appropriate headers does not trigger a download, either:
res.setHeader('Content-disposition', 'attachment; filename=filename.json');
res.setHeader('Content-type', 'application/json');
res.send({some: 'json'});
This worked for me.
I use the content type octet-stream to force the download.
Tested on chrome the json was downloaded as 'data.json'
You can't make the download with ajax according to: Handle file download from ajax post
You can use a href / window.location / location.assign. This browser is going to detect the mime type application/octet-stream and won't change the actual page only trigger the download so you can wrap it a ajax success call.
//client
const endpoint = '/download';
fetch(endpoint, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => {
//look like the json is good to download
location.assign(endpoint);
})
.catch(e => {
//json is invalid and other e
});
//server
const http = require('http');
http.createServer(function (req, res) {
const json = JSON.stringify({
test: 'test'
});
const buf = Buffer.from(json);
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-disposition': 'attachment; filename=data.json'
});
res.write(buf);
res.end();
}).listen(8888);
You can set the header to force the download, then use res.send
see those links
Force file download with php using header()
http://expressjs.com/en/api.html#res.set
Related
so I have an API with an endpoint that returns an xlsx file on post request
when I call that API from nextjs server side API it returns a corrupted zip file
but when I call it directly through postman it returns the expected xlsx file.
the call to the API from nextns looks like this:
axios.post(`${process.env.API_URI}`, formData, {
headers: {
...formData.getHeaders(),
},
responseType:"blob"
}).then(response => {
res.status(200).send(response.data)
tmpObj.removeCallback()
}).catch(err => {
console.log(err)
tmpObj.removeCallback()
})
is there a proper way to receive the xlsx file in nextjs API ... Nodejs
update:
eventually I had to set the Content-Type in axios header and the responseType in axios config object
axios.post(`${process.env.API_URI}`, formData, {
headers: {
...formData.getHeaders(),
'Content-Type': 'blob',
},
responseType:"arraybuffer"
}).then(response => {
//createthe buffer in the frontend const buffer = Buffer.from(response.data, 'base64');
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
// res.setHeader('Content-Disposition', 'attachment;filename=SheetJSNode.xlsx')
res.status(200).send(response.data)
}).catch(err => {
console.log(err)
})
I am making a Video Resizer site and I need to send the video conversion progress to the user. I read about SSE but all tutorials use get request and listen through EventSource.
In my case it is post request which contains the video file to be resized.
My Server Side Code is
const resizeVideo = async (req, res) => {
const command = ffmpeg(path.join(rootPath, 'uploads', 'videos', 'v.mp4'));
command.size('?x480').keepDAR().on('end', () => {
res.end();
console.log("Converted");
}).on('progress', function (progress) {
console.log('Processing: ' + progress.percent + '% done');
// I want to send this progress updates to react.js client.
}).save(path.join(rootPath, 'output', '1.mp4'));
}
My Client Side code is
setIsUploading(true);
const config: AxiosRequestConfig = {
responseType: 'json',
headers: {
'Content-Type': 'multipart/form-data', 'Access-Control-Allow-Origin': 'http://localhost:3000', 'Access-Control-Allow-Methods': 'POST', 'Access-Control-Allow-Headers': 'Content-Type, Authorization'
},
};
const response = await instance.post('videoconvert/resize', formData, config);
//I want to get updates here.
setIsUploading(false);
How do I achieve that?
you cant use axios you need to use SocketIO to send update live
I was using a request module and that is deprecated now. It was useful to pipe the remote files without storing in server. So looking for an alternative solution of the same function with Node-fetch, GOT, Axios etc..
import request from 'request';
(req, res) {
return request(`http://files.com/image.jpg`)
.on('error', (err) => {
log.error('error fetching img url: %O', err);
res.status(500).end('error serving this file');
})
.on('response', (urlRes) => {
if (urlRes.statusCode === 304) return;
urlRes.headers['content-disposition'] = `inline;filename="${slug}"`;
})
.pipe(res);
}
I just tried this way with node-fetch module, it works. But it doesn't pipe the response headers as like in request module. So we need to do set the required headers manually.
import fetch from 'node-fetch';
(req, res) {
fetch('http://files.com/image.jpg').then((response) => {
res.set({
'content-length': response.headers.get('content-length'),
'content-disposition': `inline;filename="${slug}"`,
'content-type': response.headers.get('content-type'),
})
response.body.pipe(res);
response.body.on('error', () => {}) // To handle failure
});
}
I have an API Server and NodeJs Server and when a file is requested NodeJs redirected the request to API Server
API Server Send the File as raw data to NodeJs
and Nodejs redirects the file to the browser
But when I checked the network data using wire shark the packet received at browser is not original as that from API Server (work in case of text files, but not in image, video, pdf, doc etc)
router.get('/GetCaseSupportDocument', function (req, res) {
var MyJsonData = {
DocId:parseInt(req.query.DocId) || 0
};
request({
url: 'http://somedomain/someurl', //URL to hit
method: 'POST',
json: MyJsonData
}, function (error, response, body) {
if (error) {
res.status(200).send('Failed');
} else {
res.status(200).send(body);
}
})
});
Can anyone tell why it changes between NodeJs to Browser?
Is there any better solution for this type of transmission?
Updated After finding solution . This works
router.get('/GetCaseSupportDocument', function (req, res) {
var MyJsonData = {
DocId:parseInt(req.query.DocId) || 0
};
request({
url: Url.CaseService + 'GetCaseSupportDocument', //URL to hit
method: 'POST',
json: MyJsonData
}).pipe(res);
})
There is a simple proxy using streams that you can try:
router.get('/GetCaseSupportDocument', function (req, res) {
var MyJsonData = {
DocId: parseInt(req.query.DocId) || 0
};
// updated the response
request({
url: 'http://somedomain/someurl', //URL to hit
method: 'POST',
json: MyJsonData
}).pipe(res);
});
More details with proxy-ing you can find on the request documentation https://github.com/request/request
I have pdf file encoded as base64 string. How to download this string to the browser as file in .pdf format?
What I have already tried:
res.set('Content-Disposition', 'attachment; filename="filename.pdf"');
res.set('Content-Type', 'application/pdf');
res.write(fileBase64String, 'base64');
I ended up to decode the pdf first and then send it to the browser as binary as follows:
(For simplicity I use node-http here but the functions are available in express as well)
const http = require('http');
http
.createServer(function(req, res) {
getEncodedPDF(function(encodedPDF) {
res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="filename.pdf"'
});
const download = Buffer.from(encodedPDF.toString('utf-8'), 'base64');
res.end(download);
});
})
.listen(1337);
What drove me nuts here was the testing with Postman:
I was using the Send Button instead of the Send and Download Button to submit the request:
Using the Send button for this request causes that the pdf file becomes corrupted after saving.
Just a reference for Express. This answer is based on ofhouse's answer.
This solution is downloading a png file. I was missing the "Content-Disposition" part, which makes the browser not display the png, but download it. png is a Buffer-object.
app.get("/image", (req, res) => {
getPng()
.then((png) => {
res.writeHead(200, {
"Content-Type": png.ContentType,
"Content-Length": png.ContentLength,
"Content-Disposition": 'attachment; filename="image.png"',
});
res.end(png.Body);
})
.catch(() => {
res.send("Couldn't load the image.");
});
});