Axios + Multer Express req.file undefined - express

I'm trying to simply upload a single file from the client (react/axios) to the server (multer / express). I've read through every "req.file undefined" and can't seem to see the same issues with my own code.
The other issue is that actually my req on the server sees the file in the "files", but multer doesn't save it and req.file is undefined.
What could be happening here?
For client I've tried both methods of sending the form data, neither work.
const onAnalyze = async () => {
if (selectedFile !== null) {
//we have a file, so that's what we're sending
var formData = new FormData();
formData.append("analyze", selectedFile);
//let res = await api.post('/analyze/upload', formData)
try {
const response = await axios({
method: "post",
url: "http://localhost:5000/analyze/upload",
data: formData,
header: { "Content-Type": "multipart/form-data" }
});
console.log(response)
} catch (error) {
console.log(error)
}
// console.log(res)
// setAnalysis(res.data)
} else if (text.length <= maxLength) {
let res = await api.post('/analyze', { text: text })
setAnalysis(res.data)
}
}
For the server it seems simple.. I just don't know. This file destination exists. req.file is always undefined
import express from 'express';
import { getMedia, createMedia } from '../controllers/media.js';
import { AnalyzeText, AnalyzeFile } from '../controllers/analyze.js'
import multer from 'multer'
const fileStorageEngine = multer.diskStorage({
destination: "uploads",
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
var upload = multer({ storage: fileStorageEngine })
const router = express.Router();
//Get All Movies and TV shows.
router.get('/', getMedia);
//Request to create a new item based on a title
router.post('/', createMedia);
//Recuist to analyze information (not sure if this should be a post or not)
router.post('/analyze', AnalyzeText)
router.post('/analyze/upload', upload.single('analyze'), (req, res) => {
console.log(req.file)
res.status(200).json('well we found it again');
});

Turns out I had another middleware running that was wrapping my file upload. Removed that, everything works.

If you're using react you may face this problem sending your request with axios. But I solved it by adding a name attribute to my input element. And removing the new formData method totally and passing the input.file[0] into axios, content-type multipart-formdata, and you must use the multer.diskStorage method. If not your image would be saved as text file

Related

When trying to upload file, axios throw "Request failed with status code 404"

I'm using VueJs3, Multer middleware for file uploading, expressJs. Its pretty new for me, so probably it is just a small mistake and i couldn't find it... After submit in console axios throw this error : "Request failed with status code 404".
And console.log(formData.append("file", this.file)) gives 'undefined'.
Some code from component:
import axios from "axios"
export default {
name: "FileUpload",
data() {
return {
file: "",
message: "",
}
},
methods: {
onSelect() {
const file = this.$refs.file.files[0]
this.file = file
},
async onSubmit() {
const formData = new FormData()
formData.append("file", this.file)
try {
await axios.post("/upload", formData)
this.message = "Upload successfully"
} catch (err) {
console.log(err)
this.message = "Something went wrong :("
}
},
},
}
And back-end:
const express = require("express")
const dotenv = require("dotenv")
const cors = require("cors")
const bodyParser = require("body-parser")
// const db = require("./app/config/db.config")
const multer = require("multer")
dotenv.config()
const app = express()
const port = process.env.PORT || 8080
const upload = multer({
dest: "./uploads",
})
var corsOptions = {
origin: "http://localhost:8081",
}
app.use(cors())
app.use(express.json())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.post("/upload", upload.single("file"), (req, res) => {
res.json({ file: req.file })
})
app.listen(port, () => console.log(`Server started on port ${port}`))
I been trying to look for solutions in some tutorials, but probably mistake is to small for my rookie eyes at the moment...
Not sure which axios version you are using but you could try switching the versions because some versions handle multipart/form-data differently.
from docs
Starting from v0.27.0, Axios supports automatic object serialization to a FormData object if the request Content-Type header is set to multipart/form-data.
I take this to mean that the header needs to be defined explicitly
const formData = new FormData()
formData.append("file", this.file)
await axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
you can also try using the postForm method
docs
await axios.postForm("/upload", {file: this.file})
// or with multipart
await axios.postForm("/upload", {'files[]': [this.file]})
I would also recommend that you look at the network panel to see if that header is included, and try sending the request directly with something like postman or insomnia to determine definitively whether the issue is on the front-end or back-end.

Uploading image - data appears like this "���"�!1A"Qaq��2��B�#" and image is blank - Next.js application upload to DigitalOcean Spaces / AWS S3

I am trying to let my users upload photos in a Next.js application.
I set up a remote database and I am writing to the database properly, but the images are appearing blank. I'm thinking it must be a problem with the format of the data coming in.
Here is my code on the front end in React:
async function handleProfileImageUpload(e) {
const file = e.target.files[0];
await fetch('/api/image/profileUpload', {
method: 'POST',
body: file,
'Content-Type': 'image/jpg',
})
.then(res => {
console.log('final:', res);
})
};
return (
<label htmlFor="file-upload">
<div>
<img src={profileImage} className="profile-image-lg dashboard-profile-image"/>
<div id="dashboard-image-hover" >Upload Image</div>
</div>
</label>
<input id="file-upload" type="file" onChange={handleProfileImageUpload}/>
)
The "file" I declare above (const file = e.target.files[0]) appears like this on console.log(file):
+ --------++-+-++-+------------+----++-+--7--7----7-���"�!1A"Qaq��2��B�#br���$34R����CSst���5����)!1"AQaq23B����
?�#��P�n�9?Y�
ޞ�p#��zE� Nk�2iH��l��]/P4��JJ!��(�#�r�Mң[ ���+���PD�HVǵ�f(*znP�>�HRT�!W��\J���$�p(Q�=JF6L�ܧZ�)�z,[�q��� *
�i�A\5*d!%6T���ͦ�#J{6�6��
k#��:JK�bꮘh�A�%=+E q\���H
q�Q��"�����B(��OЛL��B!Le6���(�� aY
�*zOV,8E�2��IC�H��*)#4է4.�ɬ(�<5��j!§eR27��
��s����IdR���V�u=�u2a��
... and so on. It's long.
I am uploading to Digital Ocean's Spaces object storage, which interfaces with AWS S3. Again, my application is written in Next.js and I am using a serverless environment.
Here is the API route I am sending it to ('/api/image/profileUpload.js'):
import AWS from 'aws-sdk';
export default async function handler(req, res) {
// get the image data
let image = req.body;
// create S3 instance with credentials
const s3 = new AWS.S3({
endpoint: new AWS.Endpoint('nyc3.digitaloceanspaces.com'),
accessKeyId: process.env.SPACES_KEY,
secretAccessKey: process.env.SPACES_SECRET,
region: 'nyc3',
});
// create parameters for upload
const uploadParams = {
Bucket: 'oscarexpert',
Key: 'asdff',
Body: image,
ContentType: "image/jpeg",
ACL: "public-read",
};
// execute upload
s3.upload(uploadParams, (err, data) => {
if (err) return console.log('reject', err)
else return console.log('resolve', data)
})
// returning arbitrary object for now
return res.json({});
};
When I console.log(image), it shows the same garbled string that I posted above, so I know it's getting the same exact data. Maybe this needs to be further parsed?
The code above is directly from a Digital Ocean tutorial but catered to my environment. I am taking note of the "Body" parameter, which is where the garbled string is being passed in.
What I've tried:
Stringifying the "image" before passing it to the Body param
Using multer-s3 to process the request on the backend
Requesting through Postman (the image comes in with the exact same garbled format)
I've spent days on this issue. Any guidance would be much appreciated.
Figured it out. I wasn't encoding the image properly in my Next.js serverless backend.
First, on the front end, I made my fetch request like this. It's important to put it in the "form" format for the next step in the backend:
async function handleProfileImageUpload(e) {
const file = e.target.files[0];
const formData = new FormData();
formData.append('file', file);
// CHECK THAT THE FILE IS PROPER FORMAT (size, type, etc)
let url = false;
await fetch(`/api/image/profileUpload`, {
method: 'POST',
body: formData,
'Content-Type': 'image/jpg',
})
}
There were several components that helped me finally do this on the backend, so I am just going to post the code I ended up with. Here's the API route:
import AWS from 'aws-sdk';
import formidable from 'formidable-serverless';
import fs from 'fs';
export const config = {
api: {
bodyParser: false,
},
};
export default async (req, res) => {
// create S3 instance with credentials
const s3 = new AWS.S3({
endpoint: new AWS.Endpoint('nyc3.digitaloceanspaces.com'),
accessKeyId: process.env.SPACES_KEY,
secretAccessKey: process.env.SPACES_SECRET,
region: 'nyc3',
});
// parse request to readable form
const form = new formidable.IncomingForm();
form.parse(req, async (err, fields, files) => {
// Account for parsing errors
if (err) return res.status(500);
// Read file
const file = fs.readFileSync(files.file.path);
// Upload the file
s3.upload({
// params
Bucket: process.env.SPACES_BUCKET,
ACL: "public-read",
Key: 'something',
Body: file,
ContentType: "image/jpeg",
})
.send((err, data) => {
if (err) {
console.log('err',err)
return res.status(500);
};
if (data) {
console.log('data',data)
return res.json({
url: data.Location,
});
};
});
});
};
If you have any questions feel free to leave a comment.

How can I make a POST request in a VSCode extension

I want to make a VSC extension that involves posting to my API, however when I write my fetch syntax out to POST to my server, it doesn't work. So I thought maybe I need to add node-fetch, so I did
npm i --save node-fetch
and it says This expression is not callable. and once again, it still can't make the POST request.
I have used axios to post to a URL:
import * as FormData from 'form-data';
import axios from 'axios';
const form = new FormData();
form.append('srcmbr', save_folderContent.srcmbr);
form.append('srcfName', save_folderContent.srcfName);
form.append('srcfLib', save_folderContent.srcfLib);
const headers = form.getHeaders();
headers['Content-length'] = await form_getLength(form);
{
const result = await axios.post(
`${serverUrl}/site/common/rmvm-srcmbr.php`, form,
{ headers, });
console.log(`delete-srcmbr ${result.data}`);
}
export function form_getLength(form: FormData)
{
return new Promise((resolve, reject) =>
{
form.getLength((err, length) =>
{
resolve(length);
});
});
}
With node-fetch you can do something like this:
const fetch = require('node-fetch');
async function main () {
const myUrl = 'https://api.example.com/route';
const myData = {};
const response = await fetch(myUrl, {
method: 'POST',
body: JSON.stringify(myData),
}).then((response) => response.json());
console.log(response);
}
main();
I forgot to add .default at the end of the axios require.
so it would be
const axios = require('axios').default;
IF YOU'RE USING TYPESCRIPT PLEASE REFER TO #RockBoro 's POST!!!

Mongoose - FindOne() inside Multer

I am sending formData to my express app like so:
itemFactory.saveItem = function(item, callback){
var formData = new FormData();
for(var i = 0; i < item.photos.length; i++){
formData.append('photos', item.photos[i]);
}
for(var key in item){
formData.append(key, item[key])
}
return $http.post('/api/item/', formData, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(callback);
};
I am saving a new item to my mongoose DB. Now everything is working perfectly. But I want to detect duplicates using mongoose findOne(), and not just let mongoose handle detecting if a duplicate key exists when writing to the DB. Because my images get uploaded regardless at this stage, if a duplicate key exists or not. Because it only detects the duplicate on save()
The problem now, in my express app, is when I am using findOne(), Multer() has not yet decoded my formData. ex:
router.post('/item', function(req, res){
Vehicle.findOne({ id: String(req.body.id) }, function(error, item){
var storage = multer.diskStorage({...})
var upload = multer({
storage: storage
}).any();
upload(req, res, function(error){
//formData is only available here via req.body
//and not at findOne() stage.
});
});
});
I cannot do the findOne inside the upload because then the files would be uploaded anyway and then only detect a duplicate.
I tried another multer().any() function for getting the formData just after the .post() but that did not seem to work. I don't think I can do this:
var detectItem = multer().any()
detectItem(req, res, function(){
Vehicle.findOne({ id: String(req.body.id) }, function(error, item){
var storage = multer.diskStorage({...})
var upload = multer({
storage: storage
}).any();
upload(req, res, function(error){
//formData is only available here via req.body
//and not at findOne() stage.
});
})
It does not seem to like me using a multer function inside a multer function.
Any advice?
You can use fileFilter option to control which files are accepted.
It could be something like this:
function filFilter (req, file, cb) {
Vehicle
.findOne({ id: req.body.id })
.then(item => {
cb(null, !item); // Skip if item exists (passing false skips file)
})
}
You could separate them into two middlewares
app.post('/item', function(req, res, next){
//this middleware is used to check duplicate
Vehicle.findOne({id:req.body.id}).then(function(item){
if(item) res.end() //if item existed, send response directly
else next() //if item not existed, continue to next middleware
})
}, function(req, res){
var storage = multer.diskStorage({...})
var upload = multer({ storage: storage }).any();
upload(req, res, function(error){
// ...
});
})

React Native IOS application can not upload image through Express+ multer server

React Native IOS application, want to upload image; from device.
RN 0.39.2
Client:
const formData = new FormData()
formData.append('files', file)
formData.append('type', 'image')
fetch(API_HOST+UPLOAD_AVATAR,{
method:'post',
headers: {'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d','Authorization': 'Bearer'+' '+token},
body: formData
})
.then(response=>response.json())
.then(data=>{
//console.log(data)
//Alert.alert(data)
})
.catch(error=>{
console.log(error)
})
Server :
var multer = require('multer');
var upload = multer();
router.post('/user', ensureAuthenticated, upload.any(), function (req, res) {
console.log(req.body);
console.log(req.files);
})
Error:
server req.body and req.files are empty.
Then I try to use RNFetchBlob.
RNFetchBlob.fetch('POST', API_HOST+UPLOAD_AVATAR, {
'Content-Type':'multipart/form-data;boundary=6ff46e0b6b5148d984f148b6542e5a5d'
'Authorization' : 'Bearer'+' '+token
}, formData)
.then((resp) => {
}).catch((err) => {
// ...
})
then error change to
NSMutableDictionary cannot be converted to NSString.
And req.body is {}, req.files is undefined
I assume you found a solution to this, if yes, could you share it?.
In any case, for the RNFetchBlob issue, I used to get the same error and I solved by changing FormData to an array. Like this:
const body = [{
name: 'data',
data: JSON.stringify(whateverData)
}, {
name: 'file',
data: filePath,
}];
…
RNFetchBlob.fetch('POST', apiEndpoint, headers, body);
Hope that helps.