Dropzonejs and Node/Multer photo uploads are not showing on the server. - javascript

Using DropzoneJs to upload files to a Node server using Multer, I am unable to save files nor see anything in the req.file or req.files object in the console.
Here is the HTML, with a sprinkle of EJS:
<div class="container">
<div class="row dropzone_input_div">
<h1 class="new_event_header">Add Your photos for <%= event.title %></h1>
<div>
<form class="dropzone needsclick dz-clickable" action="/testmedia" id="dropzoneFileUpload" name="mediaFile" enctype="multipart/form-data">
<input type="text" value="<%= event._id %>" hidden name="id">
</form>
</div>
</div>
<div class="row">
<div class="dropzone_preview">
</div>
</div>
</div>
Here is the Dropzone JS file:
Dropzone.options.dropzoneFileUpload={
paramName: "media",
parallelUploads: 30,
uploadMultiple: true,
autoProcessQueue: true
}
Here is the Node/Express/Router I am using:
const multer = require('multer');
// const upload = multer({dest:'uploads/'})
const async = require('async');
const dotenv = require('dotenv');
require('dotenv').config({silence:true});
const router = express.Router();
//Add Mongo Models to use Mongoose
const { Event, User, Moments, Instant } = require("../models");
const b2 = new B2({
accountId: process.env.B2_MASTER_APPLICATION_KEY,
applicationKey: process.env.B2_WRITE_APPLICATION_KEY
});
//router.use('/testmedia',upload.single('uploadedFile'))
router.use(multer({dest: 'uploads/'}).single('file'));
const uploads = (req,res,next) => {
console.log("hit uploads yall!");
console.log(req.file);
next();
}
const testMedia = (req,res) => {
console.log("hit testMedia route");
res.send("File uplaoded!")
}
My console.log files look like this...
hit uploads yall!
undefined
hit testMedia route
POST /testmedia 200 5.603 ms - 14
Any clue why I cannot see the files? They do not save to my /uploads file on my server (Ubuntu) and always show up undefined in the console?

My friend Eli, helped me solve this...
Steps I took:
had my browser console opened in Chrome to the network tab
uploaded a file
looked for the testmedia request
at the bottom of the window there was a Form Data section with file[0]:
(binary)
Inside my routes file I used this
const express = require('express');
const passport = require('passport');
const crypto = require('crypto');
const flash = require('express-flash');
const xoauth2 = require('xoauth2');
const B2 = require('backblaze-b2');
const async = require('async');
const multer = require('multer');
const { User } = require("../models");
const {
testMedia
} = require('../controllers/backblaze.controller')
const router = express.Router();
//*******************Middleware for Backblaze routes******************
const storeImage = multer.diskStorage({
destination: (req,file,cb) => {
cb(null, 'uploads/')
},
filename: (req,file,cb)=> {
cb(null,file.fieldname + '-'+ Date.now())
}
});
const upload = multer({storage:storeImage})
//*****THE ANSWER IS BELOW THIS LINE IN THE .single() method*****
router.post("/testmedia", upload.single('file[0]'), testMedia);
//router.post("/testmedia", upload.any(), testMedia);
The function I require is in another folder for testMedia and it looks like this
const testMedia = (req,res) => {
console.log("hit testMedia route");
res.send("File uplaoded!")
}
I'm not doing anything with the file yet, but that was my solution, the architecture is a bit different but this question is about the steps above and getting the solution through the browser and calling it correctly in the single('name') method in multer

Related

Unexpected end of form error when using Multer

I'm trying to upload an image (jpg/jpeg/png) from the browser to NodeJS. I have read through several tutorials and many posts on forums but very few seem to have this specific issue.
I've made sure to match the name provided to multer (upload.single('upload')) with the formData key (formData.append('upload', selectedFile, selectedFile.name))
I tried using headers originally, but later read that I should exclude them.
I tried submitting through a <form action="/upload" method="post" enctype="multipart/form-data"> but still got the same error.
I have found this similar question with only one answer which isn't clear
Multer gives unexpetcted end of form error and this question Unexpected end of form at Multipart._final which has no answers.
Every other question seems to be about an 'Unexpected field' or 'Unexpected end of multipart data' error which - judging from the solutions - is irrelevant here.
Below is my code...
Browser:
<body>
<input type="file" id="file_uploader" name="upload" />
<button onclick="uploadImage()" class="btn-default">SUBMIT</button>
<!-- OTHER STUFF -->
</body>
<script>
let selectedFile;
let uploadData = new FormData();
const fileInput = document.getElementById('file_uploader');
fileInput.onchange = () => {
selectedFile = fileInput.files[0];
uploadData.append('upload', selectedFile, selectedFile.name);
}
function uploadImage(){
fetch('/upload', {
method: 'POST',
body: uploadData
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error('Error: ', error);
});
}
</script>
NodeJS
let express = require('express');
const multer = require('multer');
//multer options
const upload = multer({
dest: './upload/',
limits: {
fileSize: 1000000,
}
})
const app = express();
app.post('/upload', upload.single('upload'), (req, res) => {
res.send();
}, (error, req, res, next) => {
console.log(error.message);
})
exports.app = functions.https.onRequest(app);
...And here is the error log, if it helps:
Error: Unexpected end of form
> at Multipart._final (C:\Users\p\Downloads\MyInvestmentHub\functions\node_modules\busboy\lib\types\multipart.js:588:17)
> at callFinal (node:internal/streams/writable:694:27)
> at prefinish (node:internal/streams/writable:723:7)
> at finishMaybe (node:internal/streams/writable:733:5)
> at Multipart.Writable.end (node:internal/streams/writable:631:5)
> at onend (node:internal/streams/readable:693:10)
> at processTicksAndRejections (node:internal/process/task_queues:78:11)
I haven't posted many questions as of yet, so I apologise if I'm missing something or the format is off. Let me know and I will make appropriate edits.
Thanks.
I also got the exact same error.
Before using multer I had installed express-fileupload. When I unistalled it using the command npm uninstall express-fileupload I could get rid of the error.
And if it is the same case with you don't forget to delete the commands you already added for express-fileupload module. (like requiring fileupload)
Hi there I ran into the same issue for me was the lack of a bodyParser middleware that allows our requests files to parsed into Buffers.
I was able to resolve the problem like so in express:
var bodyParser = require('body-parser')
bodyParser.json([options])
I had this problem using multer with next js api. What worked for me is, I exported an a config that sets bodyParser to false like so;
export const config = {
api: {
bodyParser: false
}
}
In my case, the cause was other middleware. Check for other middleware running before multer. For me, the issue was express-openapi-validator middleware. Once I removed that middleware, it worked as expected.
Using body-parser package worked for me:
const bodyParser = require('body-parser')
// ...
app.use(bodyParser()) // support encoded bodies
My upload single file route:
const multer = require('multer')
const express = require('express')
const router = express()
const path = require('path') // node built-in path package
// needs "app.use(bodyParser())" middleware to work
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, process.cwd() + '/public/')
},
filename: function (req, file, cb) {
// generate the public name, removing problematic characters
const originalName = encodeURIComponent(path.parse(file.originalname).name).replace(/[^a-zA-Z0-9]/g, '')
const timestamp = Date.now()
const extension = path.extname(file.originalname).toLowerCase()
cb(null, originalName + '_' + timestamp + extension)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 1 * 1024 * 1024 }, // 1 Mb
fileFilter: (req, file, callback) => {
const acceptableExtensions = ['png', 'jpg', 'jpeg', 'jpg']
if (!(acceptableExtensions.some(extension =>
path.extname(file.originalname).toLowerCase() === `.${extension}`)
)) {
return callback(new Error(`Extension not allowed, accepted extensions are ${acceptableExtensions.join(',')}`))
}
callback(null, true)
}
})
router.post('/api/upload/single', upload.single('file'), (req, res) => {
res.status(200).json(req.file)
})
module.exports = {
uploadRouter: router
}
I think this is may causes by the responsed end,so in your continuous Middleware,you can do upload file at last.
i do this resolve problems.
const upload = multer({
dest: "./uploads",
});
app.use(upload.any());
app.post(
"/upload",
(req, res, next) => {
res.end("文件上传成功");
},
upload.single("fileKey")
);
try using these it work
const express = require('express')
const app = express()
const path = require('path')
const multer = require('multer')
var filestorageEngine = multer.diskStorage({
destination: (req, file, cb) => {
cb(null,'./uploads')
},
filename:(req,file, cb) => {
cb(null,"[maues]-" + file.originalname)
}
})
var upload = multer({
storage:filestorageEngine
})
app.post('/file', upload.array('file', 3),(req, res) => {
console.log(req.file)
res.send("file uploaded successfully")
})
app.listen(5000, ()=> {
console.log("server running")
})
in my frontend or client-side removing the headers in my request. And make sure your inputs are as a formData.
For example:
let formData = new FormData();
formData.append("fileName", file);
const res = await fetch("/api/create-card", {
method: "POST",
body: formData,
})
This worked for me.
I think, the problem is in the express and body-parser module, I just eliminated it
app.use(bodyParser.text({ type: '/' }));
and it works!
Try downgrading Multer to 1.4.3. It worked for me.
See https://github.com/expressjs/multer/issues/1144

issues using base64 with showing image on client side

I am trying to upload an image and show it on the client side using base 64. I am successful in sending to mongodb but not in showing the image once it is uploaded.
My theory: I need to have the setDefaultImage function outside the setGetBaseFile function to fix to be able to see image on client side.
when I attempt to move it I get a "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. The only way arround this error that I have found is to move the function back in the parent function.
Below is my code for image.js
const mongoose = require("mongoose");
const Image = mongoose.model("gallery");
const express = require('express');
const ImageRouter = express.Router();
const DIR = './public/';
/* upload image in base64 format, thereby, directly storing it in mongodb datanase
along with images uploaded using firebase storage */
ImageRouter.route("/uploadbase")
.post((req, res, next) => {
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.body.imageData
});
newImage.save()
.then((result) => {
res.status(200).json({
success: true,
document: result
});
})
.catch((err) => next(err));
});
module.exports = ImageRouter;
Below is my client side code:
import React, { useState } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
// import Button from "react-bootstrap/Button";
// import "./postverse.css";
import Form from "react-bootstrap/Form";
import axios from "axios";
import FileBase from 'react-file-base64';
import DefaultImg from '../../assets/default-image.jpg';
const GlobalPost = () => {
const API_URL = "http://localhost:5000";
const [baseImage, UseBaseImage] = useState(DefaultImg);
const [DefaultImage, setDefaultImage] = useState("");
// function to upload image once it has been captured
setDefaultImage({
baseImage: DefaultImg
});
// function to capture base64 format of an image
function setGetBaseFile(files) {
// create a local readable base64 instance of an image
UseBaseImage({
baseImage: files.base64
});
let imageObj = {
imageName: "base-image-" + Date.now(),
imageData: files.base64.toString()
};
axios.post(`${API_URL}/image/uploadbase`, imageObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using base64 format");
UseBaseImage("base")
}
})
.catch((err) => {
alert("Error while uploading image using base64 format")
UseBaseImage("base")
});
}
return (
<div className="globalpost">
<Container className="mt-5 ml-auto mr-auto">
<h1 className="text-center">
Post to
<span className="text-success"> ShareVerse</span>
</h1>
<Form
className="shadow p-3 mb-5 bg-white rounded"
action="/search"
method="post"
encType="multipart/form-data"
>
<Form.Group controlId="formBasicVerse">
<Form.Label><h5>Upload Image</h5></Form.Label>
<FileBase type="file"
multiple={false}
onDone={setGetBaseFile}
/>
<Card.Img src={baseImage} alt="upload-image"></Card.Img>
</Form.Group>
</Form>
</Container>
</div>
);
};
export default GlobalPost;
Below is my mongoose data.schema
const mongoose = require("mongoose");
//create schema
const ImagesSchema = new mongoose.Schema({
name: {
type: String,
default: "none",
required: true
},
imageData: {
// type: mongoose.Types.ObjectId,
type: String,
required: true,
},
});
module.exports = mongoose.model("gallery", ImagesSchema);
Below is my app.js
// file includes all app level config, middleware, and supporting libraries
const express = require("express"); //import express
const app = express(); //initalise app with express
const cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const routes = require("./routes/DataRoutes");
const ImageRouter = require('./routes/image');
app.use(bodyParser.json());
//body-parser handles HTTP POST requests.
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//enable Cross-Origin Resource Sharing.
app.use(cors());
app.use(express.json());
// get log details of our app if needed
app.use(logger("dev"));
//middleware to import routes
app.use('/image', ImageRouter);
app.use("/", routes);
app.use('/search', express.static('search'));
module.exports = app;

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'
}
})

Replace HTML form action with fetch api to webserver

I managed to store files on my pc using the HTML form action attribute and then handling this request on my Express webserver.
When I now try to replace this with an eventlistener on the submit button of the form instead of using the action attribute to send the post request I can not get it to work.
I get a error message 400 bad request.
Fetch
let form = document.querySelector('#uploadForm')
let inpFile = document.querySelector('#inpFile')
form.addEventListener('submit', async event => {
event.preventDefault()
const formData = new FormData()
formData.append('inpFile', inpFile.files[0])
fetch('http://myip/upload', {
method: 'POST',
headers: {
'Content-Type' : 'multipart/form-data'
},
body: formData
}).catch(console.error)
})
HTML
<form ref='uploadForm'
id='uploadForm'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" id="inpFile" />
<input type='submit' value='Submit' />
</form>
Express Server
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.post('/upload', (req, res) => {
let sampleFile = req.files.sampleFile
sampleFile.mv(__dirname + '\\files\\' + sampleFile.name, (err) => {
if (err)
return res.status(500).send(err)
res.send('File uploaded!')
})
})
According to your html and fetch code your express code should looks like this:
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.use('/upload', fileUpload({
createParentPath: true
}));
app.post('/upload', (req, res) => {
const { inpFile } = req.files;
inpFile.mv(path.join(__dirname, 'files', inpFile.name))
.then(() => res.send('File uploaded!'))
.catch(err => res.status(500).send(err));
})
You need to bind middleware to the application:
app.use('/upload', fileUpload({
createParentPath: true
}));
And your file object should be in req.files.inpFile.
Also you need to remove headers from your fetch request.

NodeJS - Send converted file to client side to download

GOAL: Allow the user to download a PDF
Background: The below code generates a car.pdf file and stores it into the main project's directory when localhost:3000/ is loaded. This is great because I want to find a Car by id in the database, generate a handlebars template, pass the data from Car into it, and generate a PDF from the compiled HTML
Issue: Instead of saving the PDF to the main project's directory, I want it to download to the user's computer.
How can I do this?
Here is my code. I am using the NPM package: html-pdf
helpers/export-helper.js
const fs = require('fs');
const pdf = require('html-pdf');
const Handlebars = require('handlebars');
const { Car } = require('./../models/car');
var writePDF = (res) => {
Car.findById({_id: '58857e256b639400110897af'})
.then((car) => {
var source = fs.readFileSync(__dirname + '/templates/car.handlebars', 'utf8');
var template = Handlebars.compile(source);
var file = template(car);
pdf.create(file, { format: 'Letter' })
.toFile('./car.pdf', (err, res) => {
if (err) return console.log(err);
console.log(res); // { filename: '/app/businesscard.pdf' }
});
})
.catch((errors) => {
console.log(errors);
});
};
module.exports = { writePDF };
routes/home.js
const express = require('express');
const router = express.Router();
const { writePDF } = require('./../helpers/export-helpers');
router.get('/', (req, res) => {
writePDF();
});
module.exports = router;
You should use res.download for this. Like so
router.get('/', (req, res) => {
res.download('car.pdf');
});
https://expressjs.com/en/api.html#res.download
You have to pipe the created pdf with response to client side.

Categories