How to upload (pdf) file from FileReader to node js Express app - node.js

I have a (React) js app that reads a PDF file using FileReader then uses fetch to send it to a node js server running Express. When the request is received on the server side, however, the request body is undefined. What is missing from my code for this to work?
Client side:
function readFile() {
let file = fileInputRef.current.files[0];
const reader = new FileReader();
return new Promise((resolve) => {
reader.onload = function (e) {
resolve(e.target.result);
};
reader.readAsDataURL(file);//readAsDataURL, readAsArrayBuffer, or readAsBinaryString?
});
}
function handleSubmit(event) {
event.preventDefault();
readFile().then((value) => {
fetch('/gen/file', {
method: 'POST',
body: value
})
});
Server side:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const app = express();
const jsonParser = bodyParser.json()
const port = 3000;
app.post("/gen/file", function (req, res, next) {
console.log(req.body);//undefined - Why????
});
app.listen(port, function (err) {
if (err) console.log(err);
});

Client Side:
function getBase64(file,callback){
const reader = new FileReader();
reader.addEventListener('load',()=> callback(reader.result));
reader.readAsDataURL(file);
}
function handleSubmit(event) {
event.preventDefault();
let body = {};
getBase64(file,fileUrl=>{
body.file = fileUrl;
fetch('/gen/file', {
method: 'POST',
body
})
})
Server Side:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const app = express();
const jsonParser = bodyParser.json()
const port = 3000;
const fs = require('fs');
app.post("/gen/file", function (req, res, next) {
console.log(req.body);//undefined - Why????
let file = req.body.file;
let base64 = file.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
var buffer = new Buffer.from(base64[2],'base64');
fs.writeFile(__dirname+"/out.jpeg", buffer, 'base64', function (err) {
console.log(err);
});
});

Related

POST http://localhost:3000/sort net::ERR_ABORTED 500 (Internal Server Error)

I am trying to upload a file to the API, sort the numbers and then return the result in another text file that is available to download. I upload the file, and when I start the calculation I get the Internal Server Error. The API is running on port 3000 and I start the React App.js on port 3001.
Is there something I'm doing wrong?
This is the API's app.js:
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const fs = require('fs');
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
app.use(bodyParser.text({ type: 'text/plain' }));
app.post('/sort', upload.single('inputFile'), (req, res) => {
console.log(req.file)
const input = req.file.buffer.toString().split('\n').map(Number);
const result = input.sort((a, b) => b - a);
const resultText = result.join('\n');
fs.writeFile('result.txt', resultText, (err) => {
if(err) throw err;
res.send('File succesfully sorted!');
});
res.set('Content-Type', 'text/plain');
res.send(resultText);
});
app.listen(3000, () => {
console.log('API is listening on port 3000');
});
This is the React App.js:
const [inputFile, setInputFile] = useState(null);
const [result, setResult] = useState(null);
const [processingTime, setProcessingTime] = useState(null);
const handleFileUpload = (event) => {
setInputFile(event.target.files[0]);
};
const startCalculation = async (event) => {
event.preventDefault();
const startTime = performance.now();
const formData = new FormData();
formData.append('inputFile', inputFile);
console.log(inputFile)
const response = await fetch("http://localhost:3000/sort", {
method: 'POST',
body: formData,
mode: 'no-cors',
});
const data = await response.text();
console.log(data);
setResult(data);
setProcessingTime(performance.now() - startTime);
};
const handleDownload = (event) => {
event.preventDefault();
const file = new Blob([result], {
type: 'text/plain'
});
const fileURL = URL.createObjectURL(file);
const link = document.createElement('a');
link.href = fileURL;
link.download = 'result.txt';
link.click();
};
The issue is on the client you are setting the input name to inputFile, however, on the backend you are telling Multer that the input name is myFile.
Change from this:
upload.single("myFile")
To this:
upload.single("inputFile")

Why is the request body empty and the responce status is 404?

I am trying to build a web application using MVC pattern, but I have a problem with POST-request to "http://localhost:5000/api/device": postman POST request attempt
The problem only occurs on POST request, GET request is OK: get request
Code:
index.js
const express = require('express')
const sequelize = require('./db')
require('dotenv').config();
const path = require('path')
const models = require('./models/models')
const cors = require("cors")
const app = express();
const router = require('./routes/index')
const fileUpload = require('express-fileupload')
const errorHandler = require('./milddleware/ErrorHandlingMiddleware')
app.use(cors())
app.use(express.json())
app.use(express.static(path.resolve(__dirname, 'static')))
app.use(fileUpload({}))
app.use('/api', router)
app.use(errorHandler)
const PORT = process.env.PORT || 5000;
const db_start = async ()=>
{
try
{
await sequelize.authenticate();
await sequelize.sync();
app.listen(PORT, ()=>{console.log(`Server started on port ${PORT}`)})
}
catch (e)
{
console.log(e);
}
}
db_start()
routes/index.js
const Router = require('express')
const router = new Router();
const deviceRouter = require('./deviceRouter')
const typeRouter = require('./typeRouter')
const brandRouter = require('./brandRouter')
const userRouter = require('./userRouter')
router.use('/user', userRouter)
router.use('/device', deviceRouter)
router.use('/brand', brandRouter)
router.use('/type', typeRouter)
module.exports = router
routes/deviceRouter.js
const Router = require('express')
const DeviceController = require('../controllers/deviceController')
const router = new Router();
router.post('/', DeviceController.create)
router.get('/', DeviceController.getAll)
router.get('/:id', DeviceController.getOne)
module.exports = router
controllers\deviceController.js
const uuid = require('uuid')
const path = require('path')
const {Device} = require('../models/models')
const ApiError = require("../errors/ApiError")
class DeviceController
{
async create(req, res, next)
{
console.log(req);
try{
const {name, price, brandId, typeId, info} = req.body;
const {img} = req.files;
let fileName = uuid.v4() + ".jpg";
img.mv(path.resolve(__dirname, '..', 'static', fileName));
const device = await Device.create({name, price, brandId, typeId, img: fileName});
return res.json(device);
}
catch(e)
{
next(ApiError.badRequest(e.message));
}
}
async getAll(req, res)
{
const {brandId, typeId} = req.query
let devices;
if(!brandId && !typeId)
{
devices = await Device.findAll()
}
if(brandId && !typeId)
{
devices = await Device.findAll({where: {brandId}})
}
if(!brandId && typeId)
{
devices = await Device.findAll({where: {typeId}})
}
if(brandId && typeId)
{
devices = await Device.findAll({where: {brandId,typeId}})
}
return res.json(devices)
}
async getOne(req,res)
{
}
}
module.exports = new DeviceController()
I logged the request and saw that the request body came up empty and req.files is undefined.
I compared these router and controller with others and found no differences in structure.
What am I doing wrong?
You cannot retrieve files of form-data directly from req.files.
Use formidable (it's an npm package) to parse form-data and work with it easily. Here are the steps:
Run npm install formidable.
Import formidable and fs package in your controller script with:
const formidable = require('formidable')
const fs = require('fs');
Change your create function with this:
async create(req, res, next) {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
// console.log('err', err)
// console.log('fields', fields)
if (err) {
next(ApiError.badRequest(err));
return;
}
const { name, price, brandId, typeId, info } = fields; // <-- fields contain all your text data. it's like req.body
const { img } = files; // <-- it should work now!
let fileName = uuid.v4() + ".jpg";
fs.writeFileSync(path.resolve(__dirname, '..', 'static', fileName), img);
const device = await Device.create({ name, price, brandId, typeId, img: fileName });
return res.json(device);
}
}

Upload error using express-fileupload in my node js project

I am having error uploading my file. I get response that i have a file in my request body but it doesn't get uploaded
My code
const fs = require('fs')
const express = require('express')
const app = express();
const fileUpload = require('express-fileupload')
var http = require('http')
// var stream = fs.createReadStream(__dirname+'/files/1576350919658.jpeg');
var path = require('path')
app.use(fileUpload())
app.post('/',(req,res,next)=>{
console.log(req.files)
let file = req.files.image;
file.mv(__dirname+'/files', (err)=>{
if(err){
return res.send(err)
}
return res.send('File Uploaded')
})
})
app.listen(2020)
console.log('listening')
Error:
{
"errno": -4068,
"code": "EISDIR",
"syscall": "open",
"path": "D:\\wd\\javascript\\Projects\\uploadFiles\\files"
}
const fs = require('fs')
const express = require('express')
const app = express();
const fileUpload = require('express-fileupload')
var http = require('http')
// var stream = fs.createReadStream(__dirname+'/files/1576350919658.jpeg');
var path = require('path')
app.use(fileUpload())
app.post('/',(req,res,next)=>{
console.log(req.files)
let file = req.files.image;
file.mv(__dirname+'/files/ddddd.jpg', (err)=>{
if(err){
return res.send(err)
}
return res.send('File Uploaded')
})
})
app.listen(2020)
console.log('listening')
convert you image into base64 data .
var base64Data = req.body.file_data // base64 string
var file_name='123.png';
var file_dir = "assets/client_folios/"
var fs = require("fs");
if (!fs.existsSync('assets/')){
fs.mkdirSync('assets/');
}
if (!fs.existsSync(file_dir)){
fs.mkdirSync(file_dir);
}
var file_path="assets/client_folios/"+file_name
var file_path="assets/client_folios/"+file_name
fs.writeFile(file_path, base64Data, 'base64',async function(err) {
}

How to render response after stream finished pipe?

This is what I want, server is a file server, when client asks for certain file, it'll stream that file back. Instead of koi-static, I try to do this, but the dest.jpg only contains 'Not found'.
client code:
var Koa = require('koa');
var Router = require('koa-router');
const HttpStatus = require('http-status');
const fs = require('fs');
const koaBody = require('koa-body');
const request = require('request');
const tempSaveFile = fs.createWriteStream('dest.jpg');
const writeStream = request.post('http://localhost:3456/getfile/src.jpg').pipe(tempSaveFile);
writeStream.on('finish', () => {
tempSaveFile.end();
console.log('Upload successful! ');
});
server code:
var Koa = require('koa');
var Router = require('koa-router');
const HttpStatus = require('http-status');
const fs = require('fs');
const koaBody = require('koa-body');
var app = new Koa();
var router = new Router();
const serve = require('koa-static');
router
.post([`/getfile/:fileName`],
(ctx) => {
const { params: { fileName } } = ctx;
console.error(`------- server will return ${fileName} --------`);
const readStream = fs.createReadStream(fileName).pipe(ctx.res);
readStream.on('finish', () => {
console.error('---- server finished stream ----');
ctx.status = HttpStatus.OK;
});
})
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3456);
When I change the server to use koa-static, client can gets the file successfully, did diff, look the same.
I suspect server returns too fast before it finishes, but another post said this is the way to wait for pipe to finish.
callback to handle completion of pipe
Any suggestions ? thanks !
ok, I added async, working now
const multiparty = require('multiparty');
const multer = require('koa-multer');
const request = require('request');
var app = new Koa();
var router = new Router();
const serve = require('koa-static');
const streamEnd = fd => new Promise((resolve, reject) => {
fd.on('end', () => {console.error('-- 51 --'); resolve(51); });
fd.on('finish', () => {console.error('-- 53 --'); resolve(53); });
fd.on('error', reject);
});
router
.get([`/getfile/:fileName`],
async (ctx) => {
const { params: { fileName } } = ctx;
console.error(`------- server will return ${fileName} --------`);
if (fs.existsSync(fileName)) {
const readStream = fs.createReadStream(fileName).pipe(ctx.res);
await streamEnd(readStream);
ctx.status = HttpStatus.OK;
} else {
console.error(`File ${fileName} doesnot exist`);
ctx.status = HttpStatus.INTERNAL_SERVER_ERROR;
}
})
app.use(serve('./customer'));
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3456);

Stream Response into HTTP Response

I have an API that is trying to make an HTTP request to an API that streams and image back to the me, then either stream that image back to the client making the request to me or wait until the image has been streamed to me and send it all at once.
I am using Express and request-promise.
Here's a shortened version of my code.
const express = require('express');
const router = express.Router();
const request = require('request-promise');
const imgFunc = async () => {
try {
const response = await request.get({
method: 'GET',
uri: `http://localhost:8080`,
});
return response;
} catch(err) {
console.log(err);
}
};
router.get('/', async function(req, res, next) {
try {
const response = await imgFunc();
return res.send(response);
} catch (err) {
console.log(err);
}
});
module.exports = router;
The image that I get back is just what I assume is the binary data and I don't know if I need to do something at the request-promise level to make that right or when I send it back to the client.
The server that I have running at localhost:8080 mimics the actual server that I will be hitting when this is all said and done.
You could pipe the streams directly rather than using request-promise.
const express = require('express');
const router = express.Router();
const https = require('https');
router.get('/', function(req, res) {
const url = 'https://www.gravatar.com/avatar/2ea70f0c2a432ffbb9e5875039645b39?s=32&d=identicon&r=PG&f=1';
const request = https.get(url, function(response) {
const contentType = response.headers['content-type'];
console.log(contentType);
res.setHeader('Content-Type', contentType);
response.pipe(res);
});
request.on('error', function(e){
console.error(e);
});
});
module.exports = router;
Or using the request library on which request-promise is based:
const express = require('express');
const router = express.Router();
const request = require('request');
router.get('/', function(req, res) {
const url = 'https://www.gravatar.com/avatar/2ea70f0c2a432ffbb9e5875039645b39?s=32&d=identicon&r=PG&f=1';
request.get(url).pipe(res);
});
module.exports = router;
you could pipe the streams directly through axios
const express = require('express');
const router = express.Router();
const axios = require("axios");
router.get('/', function(req, res) {
const link = 'https://app.idesk360.com/media/logos/iDeskLoginTxt.png';
const arrayBuffer = await axios.get(link, {responseType: 'stream'});
const contentType = arrayBuffer.headers["content-type"];
res.setHeader('content-type', contentType);
arrayBuffer.data.pipe(res);
});
module.exports = router;

Resources