I've built a basic browser form allowing users to upload a PDF file. I then want to send that file to an Express backend. It seems like this should be a pretty basic action, but I'm unfamiliar with the end-to-end process so I'm not sure which piece is failing. I've searched through a number of SO questions/answers, but haven't found any that provide a complete solution, and I haven't been able to cobble together a solution either.
Update: It looks like the file is getting to the server, but the encoding is messed up. My guess is that FileReader.readAsText
is the wrong method to use. FileReader.readAsBinaryString
got me a little closer, but still not quite right (and it's deprecated). FileReader.readAsArrayBuffer
seems like potentially the way to go, but I'm not sure how to correctly handle the buffer in Express.
Client/Browser
The form is built in React and just uses an onChange
handler on the input itself. When a file has been added, the handler reads the file, adds it to the form data and sends the post request to the server.
// React form
<input
name="upload"
onChange={this._handleUpload}
type="file"
/>
_handleUpload = (e) => {
const { files, name } = e.target;
// Read the file
const reader = new FileReader();
reader.onload = (e) => {
const file = e.target.result;
// Now that we have the file's contents, append to the form data.
const formData = new FormData();
formData.append('file', file);
formData.append('type', name);
axios
.post('/upload', formData)
.then(res => {
// Handle the response...
})
.catch(err => console.log(err));
};
// Reading as text. Should this be something else?
reader.readAsText(files[0]);
}
Express App
The express app uses multer middleware to process the upload:
const app = express();
const upload = multer({});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.post('/upload', upload.any(), handleUpload);
Middleware
Finally, I have my own middleware that gets the file from multer. I'm testing this piece by just writing the file I've received to disk. It has contents, but it's not a readable PDF file.
const handleUpload = (req, res, next) => {
// The file shows up on req.body instead of req.file, per multer docs.
const { file } = req.body;
// File is written, but it's not a readable PDF.
const tmp = fs.writeFileSync(
path.join(__dirname, './test.pdf'),
file,
);
}
Is there some piece that I'm getting obviously wrong here? eg: Do PDFs need to be handled in a special way? Any tips for where to focus my debugging?
See if that solves your problem.
_handleUpload = (e) => {
const dataForm = new FormData();
dataForm.append('file', e.target.files[0]);
axios
.post('http://localhost:4000/test', dataForm)
.then(res => {
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<input
onChange={this._handleUpload}
type="file"
/>
</div>
)
}
server:
router.post('/test', upload.any(), (req, res) => {
console.log(req.files)
res.send({sucess: true})
})
No need to send the file type, the multer identifies the name and type for you.