Why is my Express server response truncating during SSR only? - node.js

I am using Next.js, Redux, and Express. My page has this code:
static async getInitialProps({store, isServer, pathname, query}) {
const res = await fetch('http://localhost:3001/tutorials');
const tutorials = await res.json();
store.dispatch(tutorialsReceived({tutorials}));
}
I get the React debug error saying my server response is different than my client. It's expecting a very long JSON response (252KB), and the server render is getting cut off incredibly early. I have tried two ways to send the file and am unsure why either would cut short.
// Try 1
server.get('/tutorials', (req, res) => {
fs.createReadStream('./common/content.json').pipe(res);
});
// Try 2
server.get('/tutorials', (req, res) => {
fs.readFile('./common/content.json', 'utf8', function(err, tutorials) {
res.send(tutorials);
});
});
Modifying the file to be smaller like {a:1,b:2,c:3} results in no error for my sanity check.

Looks like express is setting wrong Content-Length header in the response due to which your JSON gets chopped off. You can set it explicitly and that should work.
server.get('/tutorials', (req, res) => {
fs.readFile('./common/content.json', 'utf8', function (err, tutorials){
res.writeHead(200, {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(tutorials, 'utf8')
})
res.send(tutorials);
});
});

Related

Why can't I access req.body with multer?

My app (using vue) allows users to upload files with some info about the data to my node backend. When the user submits the form, this function is triggered:
methods: {
buttonOK () {
const formData = new FormData()
formData.append('name', this.detailFirm.name)
formData.append('description', this.detailFirm.description)
formData.append('version', this.detailFirm.version)
formData.append('date', this.detailFirm.date)
formData.append('file', this.file)
for (var [key, value] of formData.entries()) {
console.log(key, value)
}
let headers = {
'Content-Type': 'multipart/form-data',
'Accept': 'multipart/form-data'
}
this.$http.put('/firmware', formData, {headers: headers})
this.visible = false
}
The log statement shows everything that it ought to, and when this request is made, the network tab in the chrome dev tools shows the post data going through, and it has all the values it should:
name: test
description: test
version: 1
date: 0555-05-05
file: (binary)
My multer middleware looks like this:
const multer = require('multer')
const mult = multer({
dest: '/firmware'
})
module.exports = function (req, res, next) {
/* --Convert multipart/form-data to useable format within express-- */
if (req.path === '/firmware') {
mult.single('file')
console.log('MULTER MIDDLEWARE')
}
next()
}
The log statement there works, leading me to believe that multer is working.
I can't seem to access this information in back end though. Here I have tried both file and formData as the file name in mult.single('').
Here is my controller function:
let firmware = {
name: req.body.name,
version: req.body.version,
description: req.body.description,
date: req.body.date,
file: req.body.file
}
firmwareRepo.create(firmware, (err, create) => {
.............
I've read some other questions, and have made a few adjustments, but I always get an empty object when I log req.body in the controller. Please advise.
https://github.com/expressjs/multer#diskstorage
Note that req.body might not have been fully populated yet. It depends on the order that the client transmits fields and files to the server.
EDIT:
Firstly, I remember I had one problem on the frontend (React), by adding headers, which are not needed (somehow by adding formdata headers u **** up everything), here is the example:
data append stuff goes here
const data = new FormData()
data.append('id', values.id)
......
return async (dispatch) => {
const respond = await fetch('/api/postdata', {
method: 'post',
headers: {
//SEE? THIS IS EMPTY
},
body: data
})
// send form to backend
dispatch(dataSend())
}
}
Second issue could be on the backend. The thing is, that you can't just simply access file info through the req.body. You need to access it through the req.file
.post('/api/post', (req, res, next)=> {
const photo = {}
const newData = {}
uploadData(req, res, (err) => {
if(err){
console.log('error')
}
else {
Object.assign(photo, {file: req.file})
Object.assign(newData, {newData: req.body})
Then pass the photo to where you want to do something with it
const addDataController = new AddDataController(req, res, next, newAdvertData, photo)
addAdvertController.postAdvert()
}
})
Basically what I did is I separated regular data with file, and passed them further to combine and conclude the form. Sorry if this won't help, you're very close anyways!
I don't know why this worked, but everything started functioning as it should when I stopped using multer as an imported middleware, like this:
module.exports = function (req, res, next) {
/* --Convert multipart/form-data to useable format within express-- */
if (req.path === '/firmware') {
mult.single('formData')
console.log('MULTER MIDDLEWARE')
}
next()
}
and instead applied it directly to the route function, like this:
router.put('/firmware', upload.single('formData'), firmware.create) // doesn't work as standalone middleware
If anyone knows why that would be the case, please let me know.

Node Express "favicon.ico" not found error

I'm trying to download a photo through a URL passed as a query string using Express, but every time I try to use it, I get Error: Invalid URI "favicon.ico" Is there a way I can get my browser to stop requesting a favicon? For downloading images, I'm using the image-downloader package (NPM page)
Code:
app.get('/:url', (req, res) => {
let url = req.params.url;
const options = {
url: url,
dest: /path'
};
download.image(options)
.then(({ filename, image }) => {
console.log('File saved to ', filename);
})
.catch((err) => {
console.log(err);
});
res.send("Done");
});
It's probably easiest to just make a route for favicon.ico in your server.
app.get('/favico.ico', (req, res) => {
res.sendStatus(404);
});
Of course, you could actually send a valid icon too if you wanted, but this will at least keep your Express server from showing an error.
FYI, this has nothing to do with the image-downloader. This has to do with the browser requesting a favico.ico icon that it uses to show in the URL bar (and some other places in the browser UI). If your server returns a 404 for favicon.ico, the browser will use a generic icon in its UI.
If you want to make yourself a simple favico.ico, you can go here and it will help you generate one and then you can change the above route to:
app.get('/favico.ico', (req, res) => {
res.sendFile("myfavico.ico");
});
Try using another package like request module. I believe it got this type of things handled.
var fs = require('fs'),
request = require('request');
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
});
};
download('https://www.google.com/images/srpr/logo3w.png', 'google.png', function(){
console.log('done');
});

Saving HTTP GET response to a file in a express/node app

I have an express/node app which exposes a GET end point via express router something like /api/user. The response is a JSON and i want to download the JSON to a file when i hit localhost:8080/api/user.
I tried with res.download but not sure how to handle the response data with it. This could be a duplicate question but i cannot find an example especially for this use case.
When the end point is invoked in browser it should prompt for download and then should get downloaded to the default location.
router.route('/user')
.get((req, res) => {
MyService.get().then((result) => { // the get method resolves a promise with the data
// Prompt for download
}).catch((err) => {
console.log(err);
res.status(500).json({
status: 500,
data: err
});
});
});
So i was able to do this in one of the 2 ways below,
router.route('/user')
.get((req, res) => {
MyService.get().then((result) => {
res.attachment('users.csv');
/*or you can use
res.setHeader('Content-disposition', 'attachment; filename=users.csv');
res.set('Content-Type', 'text/csv');*/
res.status(200).send(result);
}).catch((err) => {
console.log(err);
res.status(500).json({
status: 500,
data: err
});
});
});
If I understand correctly, you want to save the sent data of /api/user to a file that you are sending in a route?
var fs = require('fs')
app.get("/api/user", function(req, res){
var data = fromDb()
fs.writeFileSync("/tmp/test", JSON.stringify(data))
res.send(data)
})
You need to write the JSON response to a file using the node filesystem module. You can check out an example here Writing files in Node.js
If I got you right then You can try Content-Type and Content-disposition headers like below:
res.writeHead(200, {'Content-Type': 'application/force-download','Content-disposition':attachment; filename={your_file_name}.json});
res.end(data);
NOTE :
data in res.end(data) is your json data.
{your_file_name}.json is your actual file name , give it any name.

Changing the header of the response on NodeJS using request

I have the following problem:
I want to get a static file from another server, and give it back to the user with another content-type header.
The following code works just fine, but I can't figure out a way to change the response header, though.
const request = require('request');
app.get('video', function (req, res) {
request.get('http://anotherurl.com/video-sample.mp4').pipe(res);
});
I tried to do this thing more manually, but the response was very slow.
app.get('video', function (req, res) {
request.get('http://anotherurl.com/video-sample.mp4', function(error, response, body) {
// ...
res.setHeader('content-type', 'image/png');
res.send(new Buffer(body));
});
});
Can you guys help me with that?
Thanks
Just set the response header when the 'response' event fires.
app.get('video', (req, res) => {
request.get('http://anotherurl.com/video-sample.mp4')
.on('response', response => {
res.setHeader('Content-Type', 'image/png');
// pipe response to res
// since response is an http.IncomingMessage
response.pipe(res);
});
});

NodeJs: Sending picture with Express (Multer)

I have this code
app.get('/imgs/:id', function(req, res) {
// Validate that req.params.id is 16 bytes hex string
// Get the stored image type for this image
var stream = fs.createReadStream(path.join(UPLOAD_PATH, req.params.id));
stream.on("readable", function() {
res.setHeader('Content-Type', "image/jpeg")
stream.pipe(res)
})
stream.on('error', (e) => {
res.redirect(404, "404")
})
});
Now the problem is that I always get an error of
Error: Can't set headers after they are sent.
because I used the res.setHeader function.
However, i don't know how to solve it. Let's say I want to use in a page, that has obviously the res.send() function has well,
the <img src="imgs/pic">, then I must set the header for the this page request to "image/jpeg" because otherwise the browser wouldn't know it's an image and won't show it as one.
What can I do then?
Check Express response document here. Try this code
app.get('/imgs/:id', function (req, res) {
res.sendFile(req.params.id, {root: UPLOAD_PATH, headers: {'Content-Type': 'image/jpeg'}}, function (err) {
if(err) throw err;
else console.log('sent')
})
})

Resources