How to upload image using javascript fetch api and express multer - javascript

I am working in a reactjs application where i have to upload user image. I am getting file on onChange event of file input and passing it parent component and parent component will make a post request using the data
Server side I am using express and multer for file upload and client side using fetch api to upload the image.
Thanks in advance :)

I figure it out
To upload an file/image to multer we need a form enctype="multipart/form-data" without that it wont work with multer
I am getting file from a child component then
1) i have created a empty form with the encType="mutipart/form-data"
2) when i received the file i create a new FormData(with ref to myform)
3) then append key and value in the formData
4) create fetch.post() and it works :)
for ref submitting the code
React Parent component Upload.js
import React, { Component } from 'react'
import { ImageWithoutForm } from "../app/components/ImageUpload";
export default class UploadFile extends Component {
onImageLoad(e){
console.log('onImageLoad', e.target.files[0]);
this.uploadForm(e.target.files[0]);
}
uploadForm(file){
let form = new FormData(this.refs.myForm);
form.append('myImage', file);
fetch('/upload-image', {
method: 'POST',
body: form
}).then(res => console.log('res of fetch', res));
}
render() {
return (
<div>
<h4>Upload Image</h4>
<ImageWithoutForm onImageLoad={(e)=>this.onImageLoad(e)} />
<form id="upload_form" ref="myForm" encType="multipart/form-data">
</form>
</div>
)
}
}
React Child Component with input to load the file ImageWithoutForm.js
import React, { Component } from 'react'
export class ImageWithoutForm extends Component {
handleSubmit(e){
this.props.onImageLoad(e);
}
render() {
return (
<div>
<input type="file" onChange={(e)=>this.handleSubmit(e)}/>
</div>
)
}
}
Express Route file taken from someone github repo and customized UploadImage.js
const express = require('express');
const multer = require('multer');
const path = require('path');
// Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
// Init Upload
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
// Check File Type
function checkFileType(file, cb){
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
// Init app
const app = express.Router();
// Public Folder
app.use(express.static('./public'));
app.post('/', (req, res) => {
console.log('handling upload image');
upload(req, res, (err) => {
if(err){
console.log('first err', err);
res.send({
msg: err
});
} else {
if(req.file == undefined){
console.log('Error: No File Selected!')
res.send({
msg: 'Error: No File Selected!'
});
} else {
console.log('File Uploaded!')
res.send({
msg: 'File Uploaded!',
file: `uploads/${req.file.filename}`
});
}
}
});
});
module.exports = app;
and in my express app.js just require the route file ImageUpload.js
and map to the route like this
var uploadImage = require('./routes/UploadImage');
server.use('/upload-image', uploadImage);

Related

Why is my file not being considered after multer middleware?

I am trying to upload a filename and a post (9 gag style) to my mysql database, and store the file into my project folder.
I am getting the file from the front end (console.log in the front works perfectly), then i Use nodejs + express backend to handle the file and send it to my database.
I created a multer middleware to set the filename, extension, disk location, then in a controller i am trying to get the file to send it do database. The console.log "route ok" is fine, but when i console.log req.file or req.file.filename, or req.body.file, etc... I get an undefined answer...
I really don't see what is wrong with my code, I already used it in another projet i worked fine but i was doing only the backend.
Here is my front end code (vue js) :
<template>
<div id="container">
<div id="formPost">
<button class="btn btn-primary" #click.prevent="displayFormPost = !displayFormPost">Créer un post</button>
<form v-if="displayFormPost">
<input type="text" class="form-control" placeholder="Entrer la description de l'image" v-model="messageToPost">
<input type="file" ref="file"> <button class="btn btn-primary" #click.prevent="postMessage">Poster</button>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
displayFormPost: false,
messageToPost: ''
}
},
methods: {
postMessage() {
let file = this.$refs.file.files[0];
let message = this.messageToPost;
console.log(file);
axios.post('http://localhost:3000/wall/post/', { message , file } )
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
})
}
}
}
</script>
My multer-config middleware :
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpg',
'image/png': 'png',
'image/gif': 'gif'
}
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, 'images')
},
filename: (req, file, callback) => {
const name = file.originalname.split(' ').join('_');
const extension = MIME_TYPES[files.mimetype];
callback(null, name + Date.now() + '.' + extension);
}
})
module.exports = multer({ storage }).single('image');
and my controller (i have been just trying to display filename etc so far) :
exports.postMessage = (req, res, next) => {
console.log(req.file)
console.log('routeok');
}
And my routes set with multer :
const express = require('express');
const router = express.Router();
const multer = require('../middleware/multer-config');
const wallControllers = require('../controllers/wall');
router.post('/post/', multer, wallControllers.postMessage);
module.exports = router;
Ps : no error in the console at all
Thank you !!
I think there are 2 issues:
First of all, when you set up multer you tell it to expect the file in a field named "image":
multer({ storage }).single('image');
But you never name any field as "image", not in the HTML, nor when you call axios.
Also, to send a file, you must set a proper Content-Type HTTP header and you should use the FormData API, as described in this answer.
So you may try:
const formData = new FormData();
formData.append("image", file);
formData.append("message", message);
axios.post('http://localhost:3000/wall/post/', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})

Nodejs : Get zip file from aws s3 url and manipulate files inside it after extracting

I am trying to fetch a zip file uploaded to aws s3. After that file is fetched, I have to extract it and display the names of files inside the folder. How can I achieve this? I am new to file streaming and this is what I have done till now.
import * as aws from "aws-sdk";
import express from "express";
import fs from "fs";
import request from "request";
import * as unzipper from "unzipper";
const config = {
// credentials
};
const s3Client = new aws.S3(config);
const app = express();
app.use(express.json({
limit: "1mb"
}));
app.use(express.urlencoded({
extended: true
}));
app.post("/seturl", async(req, res) => {
try {
const url = req.body.url;
request(url).pipe(fs.createWriteStream('ez.zip'));
console.log("here");
const zip = fs.createReadStream('ez.zip').pipe(unzipper.Parse({
forceStream: true
}));
for await (const entry of zip) {
const fileName = entry.path;
console.log("///////////", fileName);
const type = entry.type; // 'Directory' or 'File'
const size = entry.vars.uncompressedSize; // There is also compressedSize;
if (fileName === "this IS the file I'm looking for") {
entry.pipe(fs.createWriteStream('output/path'));
} else {
entry.autodrain();
}
}
} catch (error) {
return Promise.reject(`Error in reading ${error}`);
}
});
app.listen(5600, (err) => {
if (err) {
console.error(err);
} else {
console.log("running");
}
});
I am using the unzipper library here. If there is something better, I am open to use it. As of now, I am getting FILE ENDED error.

File not uploading in Express JS using Multer

I am creating API using express JS. Now, I have a router which will be used to upload image using multer.
Here is my router :
const multer = require('multer');
module.exports = (app) => {
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null , file.originalname);
}
});
const upload = multer({ storage: storage });
// I have also tried this but not working
// const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('image'), (req, res, next) => {
res.status(201).json({
message: "File uploaded successfully"
});
});
}
Now, from my reactjs app I am calling this router using axios like this :
const headers = {
"Content-Type": "multipart/form-data"
}
const body = new FormData();
body.append('image', this.state.selectedCategoryImage);
axios.post('http://localhost:3000/upload', body, { headers }).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
In above code this.state.selectedCategoryImage is a selected image from html <input> tag.
Now, When I call this api I am getting my response "file uploaded successfully", but I am not able to see my uploaded image anywhere in public directory. My image is not uploading.
Please anyone can help me what's the issue ?
Pass file Object not URL
URL.createObjectURL(file) // it return file url that you can use to show file preview
For upload file, send actual file in axios API as you got from file input
var file = event.target.files[0]; // return actual file
this way it actually send file object.

Could not send file object from reactjs to nodejs

I am new to both nodejs and react.
I am working on sending a selected file from react(front end) to the node (back end) where I can upload the file and convert the file into json object. but when I try to access the selected file from request.body, it says the selectedFile is undefined.
Node code:
const express = require("express");
const bodyParser = require("body-parser");
const excelToJson = require("convert-excel-to-json");
const upload = require("express-fileupload");
const cors = require("cors");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(upload());
let corsOptions = {
origin: "*",
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.post("/upload", function(request, response, next) {
if (request.body.selectedFile) {
let file = request.body.selectedFile;
let dest = __dirname + "/uploads/sample.xlsx";
file.mv(dest, function(err) {
if (err) {
response.send("File not found");
} else {
const result = excelToJson({
sourceFile: "sample.xlsx"
});
response.json(result);
}
});
} else {
response.send("File not Found");
}
});
app.listen(4001, function() {
console.log("App is listening at port 4001");
});
React code:
import React from "react";
import axios from "axios";
import logo from "./logo.svg";
import "./App.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedFile: ""
};
this.handleFileUpload = this.handleFileUpload.bind(this);
this.handleUpload = this.handleUpload.bind(this);
}
handleFileUpload = function(event) {
this.setState({
selectedFile: event.target.files[0]
});
console.log(this.state.selectedFile);
};
handleUpload = function(event) {
event.preventDefault();
console.log(this.state.selectedFile);
let data = {
selectedFile: this.state.selectedFile
};
axios
.post("http://localhost:4001/upload", data)
.then(res => console.log(res))
.catch(err => console.log(err));
};
render() {
return (
<div>
<input
type="file"
name="fileSelected"
id="fileSelected"
onChange={this.handleFileUpload}
/>
<button type="submit" onClick={this.handleUpload}>
upload
</button>
</div>
);
}
}
export default App;
You can't send a file to JSON dialect API. But you can base64 encode the file, send it to the server and decode there. This isn't the best way, because it will increase file size while transferring to the backend, and you will spend additional resources to encode/decode it. As another option, you can use FormData to send the file to the server. For this option you need to have multipart/form-data parser in the backend, I'll suggest you using busboy

Is there a way I can pass the result of a route as a field in another route?

I am trying to upload an image with multer to cloudinary and then store the resulting url in a database column. I have the following code:
The cloudinary config file:
import { config, uploader } from 'cloudinary'
import dotenv from 'dotenv';
dotenv.config();
const cloudinaryConfig = (req, res, next) => {
config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
next();
}
export { cloudinaryConfig, uploader };
The multer config file:
import multer from 'multer';
import Datauri from 'datauri';
import path from 'path';
const storage = multer.memoryStorage();
const multerUploads = multer({ storage }).single('image');
const dUri = new Datauri();
/**
* #description This function converts the buffer to data url
* #param {Object} req containing the field object
* #returns {String} The data url from the string buffer
*/
const dataUri = req => dUri.format(path.extname(req.file.originalname).toString(), req.file.buffer);
export { multerUploads, dataUri };
The entry point file(app.js)
import express from 'express';
import { urlencoded, json } from 'body-parser';
import { resolve } from 'path';
import { uploader, cloudinaryConfig } from './config/cloudinaryConfig'
import { multerUploads, dataUri } from './middlewares/multerUpload';
const app = express();
const Port = process.env.PORT || 3000;
app.use(express.static(resolve(__dirname, 'src/public')));
app.use(urlencoded({ extended: false }));
app.use(json());
app.use('*', cloudinaryConfig);
**app.post('/upload', multerUploads, (req, res) => {**
if(req.file) {
const file = dataUri(req).content;
return uploader.upload(file).then((result) => {
const image = result.url;
return res.status(200).json({
messge: 'Your image has been uploaded successfully to cloudinary',
data: {
image
}
})
}).catch((err) => res.status(400).json({
messge: 'something went wrong while processing your request',
data: {
err
}
}))
}
});
app.listen(Port, () => console.log(`Server started at http://localhost:${Port}`));
I have tried to split the functions and call them in the application routes but req.file was undefined while for the other scenario, the url wasn't generated and no error was thrown. I have also looked at the express documentation to see if I can store the result of a route in a variable but I didn't see anything like so written.
carRouter.post('/car', verifyToken, postAdchecker, cloudinaryConfig, multerUploads, postCarAd);
I want the result of the upload route which is the url generated to be passed into another route(postCarAd) as a field(imageurl). How do I make that happen?

Categories