So I figured out how to upload files using React and Node. It appears to be working, but I'm still very new to this stuff, and I can't quite understand how to access the file I just uploaded in React. I want to have it so that you use a form in React to upload a zip file, and then I want to have some scripts unzip the uploaded file and perform some actions on the contents. I have my files uploading properly, but I'm not sure how to pass the filename data into React after upload..
My server file:
const port = process.env.PORT || 5000;
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer');
const uuidv4 = require('uuid/v4');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './src/uploads');
},
filename: (req, file, cb) => {
const newFilename = `${uuidv4()}${path.extname(file.originalname)}`;
cb(null, newFilename);
},
});
var fileFilter = function (req, file, cb) {
if (
file.mimetype !== 'application/zip'
) {
req.fileValidationError = 'goes wrong on the mimetype';
return cb(new Error('mimetype does not match application/zip. upload rejected'));
}
console.log('>> fileFilter good = ',file.mimetype)
cb(null, true);
}
const upload = multer({ storage: storage, fileFilter: fileFilter });
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send();
});
app.listen(port, () => console.log(`Server listening on port ${port}`));
My React File:
import React, { Component } from 'react';
import axios from 'axios';
class UserForm extends Component {
constructor() {
super();
this.state = {
description: '',
selectedFile: '',
};
}
onChange = (e) => {
switch (e.target.name) {
case 'selectedFile':
this.setState({ selectedFile: e.target.files[0] });
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
}
onSubmit = (e) => {
e.preventDefault();
const { description, selectedFile } = this.state;
let formData = new FormData();
formData.append('description', description);
formData.append('selectedFile', selectedFile);
console.log('form data ',formData)
axios.post('/', formData)
.then((result) => {
console.log('>> (onSubmit) file upload result = ',result);
// access results...
})
.catch(function (error) {
console.log('>> ERROR FILE UPLAOD ',error);
alert('File upload failed. Please ensure you are uploading a .zip file only')
})
}
render() {
const { description, selectedFile } = this.state;
return (
<form onSubmit={this.onSubmit}>
<input
type="text"
name="description"
value={description}
onChange={this.onChange}
/>
<input
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<button type="submit">Submit</button>
</form>
);
}
}
export default UserForm;
In your case you are uploading a single file. So you need to return it from your / route like this
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send( req.file );
});
When you use Multer and upload a file like this, then req.file will be your uploaded file here, which is selectedFile. So, you need to return it to use where ever you want.
This req.file has some information like originalname, filename, path and so on. You can use those information on your front-end. For example, you can grab path (which is the full path of the uploaded file) then use it in an <img> element.
Specifically for your situation, you can hold an imagePath state:
this.state = {
description: '',
selectedFile: '',
imagePath: '',
};
Then update your state within your axois' .then method:
axios.post('/', formData)
.then((result) => {
this.setState({imagePath: result.data.path})
})
....
and use it in your component:
{this.state.imagePath && <img src={this.state.imagePath} /> }
This is a very simple example, logic can be more complex when your app gets bigger.
Related
I have 2 problems. I am trying to upload an excel file to the server when I upload an excel file I got the file on my console log but the file is not saving into the folder which I declared on my storage variable.
// Middleware
app.use(express.json())
app.use(cors());
app.use(fileupload());
app.use(express.static("files"));
app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true, parameterLimit: 50000 }));
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
var upload = multer({ storage: storage })
File upload API. The 2nd problem is I have sent some data and a file from frontend. I am getting the file only but on my req.body I got undefined. If I remove the upload.single('file') then I got the other data.
app.post("/upload-excel", upload.single('file'), async (req, res) => {
const vendor = req.body
const file = req.files.file;
const filename = file.name;
console.log(vendor);
})
Here is my frontend:
const ProductImport = function () {
const [file, setFile] = useState<any>();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [fileName, setFileName] = useState("");
const { userDetails } = UseAuth()
const saveFile = (e) => {
setFile(e.target.files[0]);
setFileName(e.target.files[0].name);
};
const uploadFile = async (e: any) => {
e.preventDefault()
const formData = new FormData();
formData.append("file", file);
formData.append("fileName", fileName);
formData.append("vendor", userDetails.role);
formData.append("store", userDetails.store);
formData.append("publisher", userDetails.email);
fetch('http://localhost:5000/upload-excel', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.insertedId) {
alert('excel Added')
}
})
.catch(error => {
console.error('Error:', error);
});
};
return (
<form onSubmit={uploadFile}>
<label htmlFor="formGroupExampleInput" className="form-label">Example label</label>
<input type="file" onChange={saveFile} />
<button type="submit">Upload</button>
</form>
)
}
backend folder structure
I'm a bit stuck with my blog app.
It works properly when I run it locally, but when it's deployed to Heroku it seems that the images that get uploaded via Multer aren't being uploaded into my images folder
When I create a post from the Heroku app, I can create the post - but the picture is missing
Below is my server code
const path = require("path");
const db = require('./config/connection');
const multer = require('multer')
const routes = require('./routes');
const app = express()
const PORT = process.env.PORT || 5000
app.use(express.urlencoded({ extended: true }));
app.use(express.json())
app.use("/images", express.static(path.join(__dirname, "./images")));
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, 'images')
},
filename: (req, file, callback) => {
callback(null, req.body.name)
}
})
const upload = multer({ storage: storage })
app.post('/upload', upload.single('file'), (req, res) => {
res.status(200).json('File has been uploaded')
})
if (process.env.NODE_ENV === 'production') {
app.use(express.static("client/build"));
}
app.use(routes)
db.once('open', () => {
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}!`);
});
});
Below here is my client code
import React, { useContext, useState } from 'react';
import './write.css';
import { Context } from '../../context/Context';
export default function Write() {
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [file, setFile] = useState('')
const { user } = useContext(Context)
const handleSubmit = async (e) => {
e.preventDefault()
const newPost = {
username: user.username,
title,
description,
}
if(file){
const data = new FormData()
const filename = Date.now() + file.name
data.append('name', filename)
data.append('file', file)
newPost.postPicture = filename
try {
await axios.post('/upload', data)
} catch (error) {
console.log(error)
}
}
try {
const response = await axios.post('/posts', newPost)
window.location.replace('/posts/'+response.data._id)
} catch (error) {
console.log(error)
}
}
return (
<div className='write'>
{file &&
<img
className='write_image'
src={URL.createObjectURL(file)}
alt=""
/>
}
<form className='write_form' onSubmit={handleSubmit}>
<div className='write_form_group'>
<label htmlFor='file_input'>
<i className='write_icon far fa-plus-square'></i>
</label>
<input type='file' id='file_input' style={{display: 'none'}} onChange={e=>setFile(e.target.files[0])} />
<input type='text' placeholder='Title' className='write_input' onChange={e=>setTitle(e.target.value)}/>
</div>
<div className="write_form_group">
<textarea placeholder='Tell your story...' type='text' className='write_input write_text' onChange={e=>setDescription(e.target.value)}></textarea>
</div>
<button className='write_submit' type='submit'>Publish</button>
</form>
</div>
)
}
I'm not sure what's going wrong, my brain is being pointed into the server side code where I use multer.diskStorage (maybe this doesn't work when the app is being hosted somewhere?)
Any advice would be amazing
Also here is the link to the whole repo on my github... because I feel like just looking at the code in these posts might be hard to follow
https://github.com/Wickette/wickettes_blog
Heroku doesn't let you do this. You need to create a bucket on Amazon's S3 service or another similar service to upload that.
You may also use cloudinary its easy to use also a free service.
All I want to achieve is to upload the image in react frontend, save it in local public/images folder in backend and display the uploaded images back in frontend. So far I have managed to upload the files to backend. I cant figure out how to display the files in frontend. I want to make a get request to send the image files to frontend
express code:
express code image
const express = require("express");
const multer = require("multer");
const cors = require("cors");
const app = express();
app.use(cors());
app.use(express.static("./public"));
var storage = multer.diskStorage({
destination: "./public/images",
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
var upload = multer({ storage: storage }).array('file');
app.post('/upload',function(req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, ()=> {
console.log("server started at port "+PORT);
});
React frontend code:
import React, { useState } from 'react';
import axios from "axios";
const ImageForm = () => {
const [file, setFile] = useState(null);
const handleFileChange = (event) => {
setFile(event.target.files);
console.log(file)
}
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData();
for(var x = 0; x<file.length; x++) {
data.append('file', file[x])
}
axios.post("http://localhost:5000/upload", data)
.then(res => {
console.log(res.statusText)
})
}
return (
<div>
<form >
<div className="form-group" >
<label htmlFor="file">Upload File:</label>
<input
className="form-control-file mb-3"
type="file" id="file"
accept=".jpg"
multiple
onChange={handleFileChange}
/>
<button
className="btn btn-primary mt-3"
onClick={handleSubmit}
>Upload</button>
</div>
</form>
{/* Display Image Here */}
</div>
);
}
export default ImageForm;
res: response
error: err
Firstly, create a state:
const [imgFile, setImgFile] = useState('');
Then, replace the following part of your code:
axios.post("http://localhost:5000/upload", data)
.then(res => {
setImgFile('http://localhost:5000/public/images/'+res.data.filename)
})
Add this after the form tag
<img src={imgFile} alt="img"/>
Just in case the image does not load, add this in the express code:
app.use('/public/images', express.static(__dirname + '/public/images/'));
So this is the first time Im trying to upload files to the server, and I started following a tutorial to builöd backend and frontend in react, but I cant get it to work.
My backend is now working with some adjustments, Ive been trying it out in postman and my uploaded files end up in the uploads folder.
But when I try it out in the browser with my frontend I cant get it to work, I only get my console.log string 'msg No file uploaded', but not the actual msg from backend.
Anyone that can see where I written wrong?
I've compared it with similar code, I've tried to change a bunch of stuff but I can't see why or what is missing?
server.js
const express = require('express')
const cors = require('cors')
const bodyParser = require('body-parser')
const fileUpload = require('express-fileupload')
const path = require("path")
var moment = require('moment')
const port = process.env.PORT || 5000
const app = express()
// enable files upload
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(fileUpload())
app.use(express.static('./public'));
//middlewares
app.use(cors())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send('hello world')
})
app.get('/upload', (req, res) => {
console.log('req.files.file', req.files.file)
res.json(req.files.file) //files should give the uploaded file
res.json(req.body.file) //body should give the name and description field
})
//Upload endpoint
app.post('/upload', (req, res) => {
try {
if (req.files === null) {
return res.status(400).json({ msg: 'No file uploaded' })
}
const file = req.files.file
const fileName = file.name
//const description = req.body
//const date = moment().add(10, 'days').calendar()
const extension = path.extname(fileName)
const allowedExtensions = /xml|jpeg|jpg|pdf/
if (!allowedExtensions.test(extension)) throw "Unsupported file type!";
//.mv() = move the file to current dir/client(react)/public
file.mv(`${__dirname}/client/public/uploads/${file.name}`)
res.json({
message: 'File uploaded successfully!',
fileName: file.name,
description: req.body,
date: moment().add(10, 'days').calendar(),
filePath: `/uploads/${file.name}`
})
} catch (err) { //catch if path doesn't exist
console.error(err)
//500 server error
return res.status(500).send(err)
}
})
//make uploads directory static
app.use(express.static('uploads'));
//Delete endpoint
//app.delete('/upload')
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`)
})
fileUpload.js
import React, { useState } from 'react'
import axios from 'axios'
const FileUpload = () => {
const [file, setFile] = useState('')
const [fileName, setFileName] = useState('Choose File')
const [uploadedFile, setUploadedFile] = useState({});
const onChange = (e) => {
setFile(e.target.files[0])
setFileName(e.target.files[0].name) //this should change the name in {fileName}
}
const onSubmit = async e => {
e.preventDefault()
const formData = new FormData()
formData.append('file', file)
console.log('file', file)
try {
const res = await axios.post('/upload', formData, {
header: {
'Content-Type': 'multipart/form-data'
}
})
const { fileName, filePath } = res.data
console.log('res.data', res.data)
setUploadedFile({ fileName, filePath })
console.log('setUploadedFile', fileName, filePath)
} catch (err) {
if (err.response.status === 500) {
console.log('There was a problem with the server')
} else {
console.log('msg No file uploaded', err.response.data.msg)
}
}
}
return (
<>
<form onSubmit={onSubmit}>
<div className='custom-file'>
<input
type='file'
name='file'
className='custom-file-input'
id='customFile'
onChange={onChange} />
<label
className='custom-file-label'
htmlFor='customFile'>{fileName}
</label>
</div>
<input
type='submit'
value='Upload file'
className='btn btn-secondary btn-block mt-4'
/>
</form>
{uploadedFile ? (
<div>
<h3>{uploadedFile.fileName}</h3>
<img scr={uploadedFile.filePath} alt='' />
</div>
) : null}
</>
)
}
export default FileUpload
The backend server is running on the port 5000(Assuming you haven't set the variable of process.env.PORT), therefore the url should be http://localhost:5000 or http://127.0.0.1:5000.
The axios.post('/upload', ...) is posting data to react url. Hence 404 error.
const res = await axios.post('http://localhost:5000/upload', formData, {
header: {
'Content-Type': 'multipart/form-data'
}
})
I'm trying to save the image file using multer package.
Here is our registration.js which contains the registration form. The following scripts have no errors, but we don't know how to access the file in url or show the image to users (exactly how to save it with its MIME format).
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class Registration extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
var config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
var formData = new FormData();
var imagefile = document.getElementById('profilePicture');
formData.append('name',document.getElementById('name').value);
formData.append('email',document.getElementById('email').value);
formData.append('telegramId',document.getElementById('telegramId').value);
formData.append('password',document.getElementById('password').value);
formData.append('image', imagefile.files[0]);
console.log(formData);
axios.post('/registration', formData,config).then(function(res){
console.log(res.data+'res');
}).catch(console.error);
}
render() {
return (<form className="registrationForm">
<input type="text" name="name" id="name" required="required" placeholder="name" />
<br/>
<input type="email" id="email" required="required" placeholder="email"/>
<br/>
<input type="text" id="telegramId" required="required" placeholder="telegramID"/>
<br/>
<input type="password" id="password" required="required" placeholder="password"/>
<br/>
<input type="file" id="profilePicture"/>
<br/>
<button className="registerButton" onClick={this.handleSubmit}>Register</button>
</form>)
};
}
and this is server side code:
var multer = require('multer')
var upload = multer({ dest: '../assets/uploads/' })
app.post('/registration',upload.single('image'), (req, res) => {
// console.log(req.body);
console.log(req.file);
});
and this is what the console has logged!
{ fieldname: 'image',
originalname: 'sharif-logo-png-transparent.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'e098a8370f444f321acd9aae668538fd',
path: 'uploads\\e098a8370f444f321acd9aae668538fd',
size: 271654
}
multer already takes care of saving the file to disk, so all you need to do is make that folder accessible to be used in <img> tags (or whatever you'll want to use to display them). A good solution would be express.static:
const { static } = require('express');
app.use('/images/', static('../assets/uploads/'));
Then you can use them in your client-side code:
<img src="/images/e098a8370f444f321acd9aae668538fd"></img>
You don't have to handle the file at all in the .post('/registration') handler.
const express = require('express');
const multer = require('multer');
const path = require('path');
const router = express.Router()
// Image Upload
const imageStorage = multer.diskStorage({
destination: 'images', // Destination to store image
filename: (req, file, cb) => {
cb(null, file.fieldname + '_' + Date.now() + path.extname(file.originalname))
// file.fieldname is name of the field (image), path.extname get the uploaded file extension
}
});
const imageUpload = multer({
storage: imageStorage,
limits: {
fileSize: 1000000 // 1000000 Bytes = 1 MB
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(png|jpg)$/)) { // upload only png and jpg format
return cb(new Error('Please upload a Image'))
}
cb(undefined, true)
}
})
// For Single image upload
router.post('/uploadImage', imageUpload.single('image'), (req, res) => {
res.send(req.file)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
// For Multiple image upload
router.post('/uploadBulkImage', imageUpload.array('images', 4), (req, res) => {
res.send(req.files)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
// ---------------------------------------------------------------------------- //
// Video Upload
const videoStorage = multer.diskStorage({
destination: 'videos', // Destination to store video
filename: (req, file, cb) => {
cb(null, file.fieldname + '_' + Date.now() + path.extname(file.originalname))
}
});
const videoUpload = multer({
storage: videoStorage,
limits: {
fileSize: 10000000 // 10000000 Bytes = 10 MB
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(mp4|MPEG-4)$/)) { // upload only mp4 and mkv format
return cb(new Error('Please upload a Video'))
}
cb(undefined, true)
}
})
router.post('/uploadVideo', videoUpload.single('video'), (req, res) => {
res.send(req.file)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
module.exports = router