When trying to submit an audio file to backend from the front end im getting two errors error: Error: Unexpected end of form
error: uploading file: Error: Failed to upload audio file
im using form data to send an audio file on the front end and multer and gridfs on the backend. does anybody know why i am getting this error.
When trying to submit an audio file to backend from the front end im getting this error: Error: Unexpected end of form, im using form data to send an audio file on the front end and multer and gridfs on the backend. does anybody know why i am getting this error.
here is my app.js
import React, { useState } from 'react';
const App = () => {
const [selectedFile, setSelectedFile] = useState(null);
const handleFileChange = (event) => {
setSelectedFile(event.target.files[0]);
}
const handleFormSubmit = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('audioFile', selectedFile);
fetch('http://localhost:4002/audio', {
method: 'POST',
body: formData,
})
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Failed to upload audio file');
}
})
.then(data => {
console.log('File uploaded successfully:', data);
// Do something with the response data
})
.catch(error => {
console.error('Error uploading file:', error);
});
}
return (
<div className="flex h-[100vh] w-[100%] items-center justify-center">
<form onSubmit={handleFormSubmit} encType="multipart/form-data">
<input type="file" onChange={handleFileChange} />
<button type="submit" disabled={!selectedFile}>Upload</button>
</form>
</div>
);
};
export default App;
here is my sever.js
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const { GridFsStorage } = require('multer-gridfs-storage');
const Grid = require('gridfs-stream');
const cors = require('cors');
const path = require('path')
const bodyParser = require("body-parser")
const app = express();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect('mongodb+srv://jordandeeds31:Jd400089#cluster0.ibeioeb.mongodb.net/?retryWrites=true&w=majority', {
useUnifiedTopology: true,
useNewUrlParser: true,
}).then(() => {
console.log('Connected to MongoDB');
}).catch((err) => {
console.error(err);
});
const conn = mongoose.connection;
let gfs;
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('audioFiles');
});
const storage = new GridFsStorage({
url: 'mongodb+srv://jordandeeds31:Jd400089#cluster0.ibeioeb.mongodb.net/grid?retryWrites=true&w=majority',
file: (req, file) => {
return {
filename: file.originalname,
bucketName: 'audioFiles'
};
}
});
const upload = multer({
storage: storage,
limits: {
fileSize: 90 * 1024 * 1024 // 10MB
},
fileFilter: (req, file, cb) => {
if (file.mimetype.startsWith('audio/')) {
cb(null, true);
} else {
cb(new Error('File type not supported.'));
}
}
});
// Add this middleware before the POST route
app.use(upload.any());
const audioFileSchema = new mongoose.Schema({
fileUrl: {
type: String,
required: true
},
audioData: {
type: Buffer,
required: true
}
});
const AudioFile = mongoose.model('AudioFile', audioFileSchema);
app.post('/audio', upload.single('audioFile'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ message: 'No file uploaded.' });
}
const audioFile = new AudioFile({
fileUrl: `http://localhost:4002/audio/${req.file.filename}`,
audioData: req.file.buffer
});
console.log('req.file.buffer:', req.file.buffer);
const savedAudioFile = await audioFile.save();
res.json({ fileUrl: savedAudioFile.fileUrl });
} catch (err) {
console.error(err);
if (err instanceof multer.MulterError) {
res.status(400).json({ message: 'File upload error.' });
} else if (err.message === 'File type not supported.') {
res.status(400).json({ message: err.message });
} else {
res.status(500).send('Internal Server Error');
}
}
});
app.get('/audio/:filename', (req, res) => {
const { filename } = req.params;
const readStream = gfs.createReadStream({ filename });
readStream.on('error', (err) => {
console.error(err);
res.status(404).send('File not found.');
});
readStream.pipe(res);
});
app.listen(4002, () => {
console.log('Listening on port 4002');
});
Related
Why am i getting this error?
I am trying to send the audio file I have on the front end using form data to the backend, where I'm using multer and gridfs to send the file to mongoDB. I have been a lot issues trying to do this with an audio file.
Why am i getting this error? I am trying to send the audio file. I have on the front end using form data to the backend where I'm using multer and gridfs to send the file to mongoDB. I have been a lot issues trying to do this with an audio file.
App.js
import React, { useState } from 'react';
function AudioUpload() {
const [file, setFile] = useState(null);
const handleFileChange = (e) => {
setFile(e.target.files[0]);
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('audio', file);
try {
const response = await fetch('http://localhost:4003/upload', {
method: 'POST',
body: formData,
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<input type="file" id="audio" name="audio" accept="audio/*" onChange={handleFileChange} />
</div>
<button type="submit" disabled={!file}>Upload</button>
</form>
</div>
);
}
export default AudioUpload;
Server.js
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const Grid = require('gridfs-stream');
const { GridFsStorage } = require('multer-gridfs-storage');
const cors = require("cors");
const app = express();
app.use(cors());
const conn = mongoose.createConnection('mongodb+srv://jordandeeds31:Jd400089#cluster0.iwva5ci.mongodb.net/grid2?retryWrites=true&w=majority', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
let gfs;
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('audioFiles');
console.log("connected to db");
});
const storage = new GridFsStorage({
url: 'mongodb://localhost/mydatabase',
file: (req, file) => {
return {
filename: file.originalname,
bucketName: 'audioFiles',
};
},
});
const upload = multer({ storage: storage });
const audioSchema = new mongoose.Schema({
filename: { type: String, required: true },
contentType: { type: String, required: true },
fileId: { type: mongoose.Schema.Types.ObjectId, required: true },
});
const Audio = conn.model('Audio', audioSchema);
app.post('/upload', upload.single('audio'), async (req, res) => {
try {
const { originalname, mimetype } = req.file;
const fileId = req.file.id;
const audio = new Audio({
filename: originalname,
contentType: mimetype,
fileId,
});
await audio.save();
res.status(200).json({ message: 'File uploaded successfully' });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Error uploading file' });
}
});
app.listen(4003, () => {
console.log('Server listening on port 4003');
});
When trying to retrieve my audio file from the backend server and then display it on the front I keep getting an error stating that GET http://localhost:4004/audio/song.m4a 404 (Not Found). I am using GridFS and multer to store the audio files inside of mongoDB. I was able to successfully send my audio file inside of the database but unable to retrieve the audio file and display it on the front end.
here is my front end code
import React, { useState, useEffect } from 'react';
function AudioUpload() {
const [audioFiles, setAudioFiles] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('http://localhost:4004/audio');
const data = await response.json();
console.log(data);
if (data.length > 0) {
setAudioFiles(data);
}
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
return (
<div>
{audioFiles.map((audio, index) => (
<div key={index}>
<audio src={`http://localhost:4004/audio/${audio.filename}`} controls>
Your browser does not support the audio element.
</audio>
</div>
))}
</div>
);
}
export default AudioUpload;
and here is my backend code:
const express = require('express');
const mongoose = require('mongoose');
const multer = require('multer');
const Grid = require('gridfs-stream');
const { GridFsStorage } = require('multer-gridfs-storage');
const cors = require("cors");
const app = express();
app.use(cors());
const conn = mongoose.createConnection('mongodb+srv://jordandeeds31:Jd400089#cluster0.iwva5ci.mongodb.net/grid2?retryWrites=true&w=majority', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
let gfs;
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('audioFiles');
console.log("connected to db");
});
const storage = new GridFsStorage({
url: 'mongodb+srv://jordandeeds31:Jd400089#cluster0.iwva5ci.mongodb.net/grid2?retryWrites=true&w=majority',
file: (req, file) => {
return {
filename: file.originalname,
bucketName: 'audioFiles',
};
},
});
const upload = multer({ storage: storage });
const audioSchema = new mongoose.Schema({
filename: { type: String, required: true },
contentType: { type: String, required: true },
fileId: { type: mongoose.Schema.Types.ObjectId, required: true },
});
const Audio = conn.model('Audio', audioSchema);
app.post('/upload', upload.single('audio'), async (req, res) => {
try {
const { originalname, mimetype } = req.file;
const fileId = req.file.id;
const audio = new Audio({
filename: originalname,
contentType: mimetype,
fileId,
});
await audio.save();
res.status(200).json({ message: 'File uploaded successfully' });
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Error uploading file' });
}
});
app.get('/audio', async (req, res) => {
try {
const audioList = await Audio.find();
console.log(audioList)
if (!audioList) {
return res.status(404).json({ message: 'No audio files found' });
}
res.json(audioList);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Error retrieving files' });
}
});
app.listen(4004, () => {
console.log('Server listening on port 4004');
});
== updated question on 9/9 ===
Tried to use Multer directly without the Middleware like before and using Postman to upload the images.
From Nodejs, req return
files: [Object: null prototype] {
imagebackup: [ [Object] ],
imagebanner: [ [Object] ]
},
However, when I console req.file
it showing "undefined"
new file-routers.js as below:
const express = require('express');
const multer = require('multer');
const router = express.Router();
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "imagebackup"}, {name: "imagebanner"}]);
router.post('/file', (req, res)=>{
upload(req, res, (err) => {
console.log(req) // return Files [object null]
console.log(req.file) // return "undefined"
if(err) throw err;
})
});
**Weird thing is, by using upload.single(), everything works just fine. **
==
==
===== Here is the old code & can't solve it =====
It return an error
MulterError: Unexpected field
at wrappedFileFilter (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\index.js:40:19)
at Busboy.<anonymous> (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\lib\make-middleware.js:114:7)
at Busboy.emit (node:events:379:20)
On the form submit, I need upload 2 images from different input field, create new prefix on GCS, and also store the image's name and other's details to be sent in request.body.
From the front-end part, I using Fetch as below:
const getFormContianer = document.getElementById("get_form")
async function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api err: '+data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
getFormContianer.addEventListener('submit', handleForm)
index.html
<form id="get_form">
<label for="video_url">video_url</label>
<input name="video_url" type="text" id="video_url" value=""><br>
<label for="image_backup">image_backup</label>
<input name="image_backup" type="file" id="image_backup" value=""><br>
<label for="image_banner">image_banner</label>
<input name="image_banner" type="file" id="image_banner" value=""><br>
</form>
<input type="submit" id="handle_submit">
Nodejs
multer middleware
const util = require("util");
const multer = require("multer");
let processFile = multer({
storage: multer.memoryStorage()
}).fields([{ name: "image_backup" }, { name: "image_banner" }])
let processFileMiddleware = util.promisify(processFile);
module.exports = processFileMiddleware;
handling Upload
const handleUploadImages = async (req, res) =>{
try {
await processFile(req, res);
if (!req.file) {
return res.status(400).send({ message: "Please upload a file!" });
}
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(newFolderPath + req.file.originalname);
const blobStream = blob.createWriteStream({
resumable: false,
});
blobStream.on("error", (err) => {
res.status(500).send({ message: err.message });
});
blobStream.on("finish", async (data) => {
// Create URL for directly file access via HTTP.
const publicUrl = format(
`https://storage.googleapis.com/${bucket.name}/${newFolderPath}/${blob.name}`
);
try {
// Make the file public
await bucket.file(newFolderPath + req.file.originalname).makePublic();
} catch {
return res.status(500).send({
message:
`Uploaded the file successfully: ${newFolderPath + req.file.originalname}, but public access is denied!`,
url: publicUrl,
});
}
res.status(200).send({
message: "Uploaded the file successfully: " + newFolderPath + req.file.originalname,
url: publicUrl,
});
});
blobStream.end(req.file.buffer);
} catch (err) {
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
}
I did use express json and urlencoded on index.js
const express = require('express');
const cors = require('cors');
const config = require('./config')
const app = express()
const templates = require('./routes/templates-routes');
const files = require('./routes/files-routes');
// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'))
app.use('/api', templates.routes);
app.use('/create', files.routes);
app.listen(config.port, () => {
console.log(`Example app listening at http://localhost:${config.port}`)
})
Hope that can get some suggestion on this, thank you!
Where is body: dataForm declared? That error arises when you try to upload a field that is not mentioned in fields:
fields([{ name: "image_backup" }, { name: "image_banner" }])
Make sure your multipart form has these 2 fields only for uploading files.
I suggest you to check this post in which they discuss the same issue.
In order to solve it they basically formatted the file that need to be uploaded.
formData.append("type_of_the_file", uploadfile);
Finally solved the issue after few days keep trying.
for front-end POST rest API, passing files by appending file itself and the name.
function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
dataForm.append("image_backup", document.getElementById("image_backup").files[0]);
dataForm.append("image_banner", document.getElementById("image_banner").files[0]);
dataForm.append("image_banner_name", document.getElementById("image_banner").value.replace(/^.*[\\\/]/, ''));
dataForm.append("image_backup_name", document.getElementById("image_backup").value.replace(/^.*[\\\/]/, ''));
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api data: '+ data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
on Nodejs
const multer = require('multer');
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "image_backup"}, {name: "image_banner"}]);
const startCreatefiles = async(req, res, next) =>{
upload(req, res, (err) => {
console.log(req.body);
console.log(req.files);
})
}
Then successfully get the text form data and file itself.
I am trying to let the user update their profile with their own avatar but for some reason, it doesn't work. I am sending a FormData from the front-end and catch it with nodeJs before storing it to MongoDB. user has 3 options to update: name, about, avatar. Whenever I try to update the name and about it works just fine but when I am uploading an avatar I get a 500 error. Can you guys take a look? FYI I am a begginer.
Here is my code:
FRONT-END
const handleFileInputChange = (e) => {
const file = e.target.files[0];
previewFile(file);
setUser({
...user,
avatar: file,
});
};
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('name', user.name);
formData.append('about', user.about);
formData.append('avatar', user.avatar);
editUserProfile(formData); // external axios.patch f.
};
const editUserProfile = async (formData) => {
try {
await axios.patch('api/v1/users/me', formData, {
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
dispatch({
type: EDIT_USER,
});
} catch (err) {
console.log(err);
}
};
<form onSubmit={onSubmit} encType="multipart/form-data">
<input
// accept="image/*"
accept=".png, .jpg, .jpeg"
type="file"
name="avatar"
// value={fileInputState}
onChange={handleFileInputChange}
style={{ display: 'none' }}
id="icon-button-file"
/>
...
BACK-END
router.patch('api/v1/users/me', protect, upload.single('avatar'), getMe, editUser = async (req, res) => {
try {
const { name, about } = req.body;
const profileFileds = {};
if (name) profileFileds.name = name;
if (about) profileFileds.about = about;
if (req.file) profileFileds.avatar = req.file.filename;
const user = await User.findByIdAndUpdate(req.params.id, profileFileds, {
new: true,
runValidators: true,
});
if (!user) {
return next(
new custom error...
);
}
res.status(200).json({
status: 'success',
data: {
user,
},
});
} catch (err) {
console.log(err) error 400 ...
}
}););
Sorry guys if it's a long code and I really appreciate it, I am struggling 2 days now and can't figure it out
//MULTER CONFIG just in case but it shouldn't be a problem with it
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/images');
},
filename: function (req, file, cb) {
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname));
},
});
const fileFilter = (req, file, cb) => {
const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (allowedFileTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 2,
},
fileFilter: fileFilter,
});
I'm trying to upload an image with expo picker image to a nodejs server.
The problem is I never receive the image. I tried so many things I'm desesperate :(
Here is my code :
React-Native
postImage = async (image) => {
const photo = {
uri: image.uri,
type: "image/jpg",
name: "photo.jpg",
};
const form = new FormData();
form.append("test", photo);
axios.post(
url,
{
body: form,
headers: {
'Content-Type': 'image/jpeg',
}
}
)
.then((responseData) => {
console.log("Succes "+ responseData)
})
.catch((error) => {
console.log("ERROR " + error)
});
}
pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
// mediaTypes: ImagePicker.MediaTypeOptions.All,
// allowsEditing: true,
// aspect: [4, 3],
quality: 1
});
if (!result.cancelled) {
try {
await this.postImage(result);
} catch (e) {
console.log(e);
}
this.setState({ image: result.uri });
}
};
It always works.
And here the nodejs code
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const multer = require('multer');
const fs = require("fs");
const app = express();
const upload = multer({
dest: "upload/",
});
// app.use(upload.single("test"));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cors());
app.listen(8080, () => {
console.log("running ...")
})
app.post("/upload", upload.single("photo.jpg"), async (req, res) => {
console.log("body =>", req.body);
console.log('files => ', req.files);
console.log("file =>", req.file);
// const oldpath = req.body.;
// const newpath = '/Users/mperrin/test/test-native/test-upload-photo/server/lol.jpg';
// fs.rename(oldpath, newpath, (err) => {
// if (err) {
// throw err;
// }
// res.write('File uploaded and moved!');
// res.sendStatus(200);
// });
res.sendStatus(200);
});
I always see this in the console and I don't know what to do with that ...
body => {
body: { _parts: [ [Array] ] },
headers: { 'Content-Type': 'image/jpeg' }
}
At the moment only the folder "upload" is created.
I don't know where I can get the files, I guess I'm really missing something but I don't know what.
Thanks for help guys !
I've never used multer before but after a quick review of the docs it looks like you need to have the same name in the headers as you're expecting in the node side post
Right now, in your header you have
name: 'photo.jpg'
and the following in your node post
upload.single("test")
Your post is looking for something with the name 'test' not 'photo.jpg' and you're sending 'photo.jpg'
Try it out and let me know how it goes.
Edit: My mistake, you may have add
name: "test"
to the headers here instead of in the photo object:
axios.post(
url,
{
body: form,
headers: {
'Content-Type': 'image/jpeg',
}
})