I am trying to post a file into API my file is shown in the console, but in API payloads it is showing [object file] on uploading.
This is a function for taking file
handleFileChange = (e) => {
const files = e.target.files[0];
this.setState({ document: files });
console.log("Guru4666", this.state.document);
};
The state is then pushed into an object called award value.
SubmitAward() {
var awardValue = {
title: this.state.awardTitle,
award_img: this.state.document,
};
console.log("awardValue", awardValue);
this.state.awarddata.push(awardValue);
}
Then the API is called.
instructor_register() {
var titlearr = [];
var awardimgarr= [];
const UserToken = localStorage.getItem("UserToken");
this.state.awarddata.map((Data) => {
console.log("AwardDataa", Data);
titlearr.push(Data.title);
awardimgarr.push(Data.award_img);
});
const formdata = new FormData();
formdata.append("first_name",this.state.firstname)
formdata.append("instructor_award_name",titlearr)
formdata.append("instructor_award_img",awardimgarr)
axios({
method: "post",
url: `${API_AUTH_URL}instructor-register`,
data: formdata,
headers: { Authorization: "Bearer" + UserToken },
})
.then((response) => {
//this.setState({ modalVisibleLoader: false })
console.log("response.....", response.data);
}
Payload is
instructor_award_img: [object File]
You are attempting to append an array to the formdata object. This isn't a supported data type, so it gets converted to a string.
Supported data types are strings and blobs (files are a type of blob).
Append the file instead.
If you want to append multiple files, do it by looping over the array and appending each file in turn.
Related
I am running into an issue where when I want to upload an image to s3 bucket nothing goes through.
Basically the only message I get is
API resolved without sending a response for /api/upload/uploadPhoto, this may result in stalled requests.
In the front end, I have an input which can take multiple files ( mainly images ) and then those are stored in event.target.files.
I have a function that stores each file in a state array, and with the button submit it sends a post request to my next.js API.
Here's the logic on the front end:
This function handles the photos, so whenever I add a photo it will automatically add it to the listingPhotos state:
const handleListingPhotos = async (e: any) => {
setMessage(null);
let file = e.target.files;
console.log("hello", file);
for (let i = 0; i < file.length; i++) {
const fileType = file[i]["type"];
const validImageTypes = ["image/jpeg", "image/png"];
if (validImageTypes.includes(fileType)) {
setListingPhotos((prev: any) => {
return [...prev, file[i]];
});
} else {
setMessage("Only images are accepted");
}
}
};
Once the photos are stored in the state, I am able to see the data of the files in the browsers console.log. I run the onSubmit to call the POST API:
const handleSubmit = async (e: any) => {
e.preventDefault();
const formData = new FormData();
formData.append("files[]", listingPhotos);
await fetch(`/api/upload/uploadPhoto`, {
method: "POST",
headers: { "Content-Type": "multipart/form-data" },
body: formData,
}).then((res) => res.json());
};
console.log("listingphotos:", listingPhotos);
Which then uses this logic to upload to the S3 Bucket, but the issue is that when I log req.body I am getting this type of information:
req.body ------WebKitFormBoundarydsKofVokaJRIbco1
Content-Disposition: form-data; name="files[]"
[object File][object File][object File][object File]
------WebKitFormBoundarydsKofVokaJRIbco1--
api/upload/UploadPhoto logic:
import { NextApiRequest, NextApiResponse } from "next";
const AWS = require("aws-sdk");
const access = {
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
};
// creates an S3 Client
const s3 = new AWS.S3({ region: "region", credentials: access });
export default async function uploadPhoto(
req: NextApiRequest,
res: NextApiResponse
) {
// take info from parent page
// console.log("req.body: ", req.body);
if (req.method === "POST") {
console.log("req.body", req.body);
let body = req.body;
let headers = req.headers;
let contentType = headers["Content-Type"] || headers["content-type"];
// check for correct content-type
if (!contentType.startsWith("multipart/form-data")) {
return { statusCode: 400, body: "Invalid content type" };
}
let boundary = contentType.replace("multipart/form-data; boundary=", "");
let parts = body.split(boundary);
for (let part of parts) {
if (part.startsWith("Content-Disposition")) {
let [fileData] = part.split("\r\n\r\n");
fileData = fileData.slice(0, -2);
let [fileName] = part.split("filename=");
fileName = fileName.slice(1, -1);
let params = {
Bucket: "RANDOM BUCKET NAME",
Key: fileName,
Body: fileData,
ContentType: { "image/png": "image/jpg" },
};
// Need to set the PARAMS for the upload
await s3.putObject(params);
console.log(
"Successfully uploaded object: " + params.Bucket + "/" + params.Key
);
}
}
return {
statusCode: 200,
body: "File uploaded",
};
// Uploads the files to S3
}
}
I was able to find a way to read if the files were correctly displayed.
req.body {
fileName: 'b699417375e46286e5a30fc252b9b5eb.png',
fileType: 'image/png'
}
POST request code was changed to the followng:
const s3Promises = Array.from(listingPhotos).map(async (file) => {
const signedUrlRes = await fetch(`/api/upload/uploadPhoto`, {
method: "POST",
body: JSON.stringify({
fileName: file.name,
fileType: file.type,
}),
headers: { "Content-Type": "application/json" },
});
Obviously, this is not the solution but it's part of it. The only problem I am running into right now is handling CORS in order to see if the files are sent to the bucket.
I have a problem with the PDF file I get from the API in response to a Json GET request.
It does get a good string in Json, however, which makes the PDF file that appears corrupted. I tried to convert the response to a string but it didn't do anything.
Here is my code:
getPDF() {
axios
.get(apiurl + "api/Dok", { params: { }, headers: { } })
.then(response => {
const res = response.data.fileData;
const pdfcode = res.toString();
this.convertPDF(pdfcode)
}
)
.catch(error => {
alert('error')
});
}
convertPDF(value) {
const file = new Blob(
[value],
{type: 'application/pdf'});
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
So if i'm import the pdf file from local and add it to this function istead of url in console i'm getting long string response
JVBERi0xLjUNCjQgMCBvYmoNCjw8L1R5cGUgL1BhZ2UvUGFyZW50IDMgMCBSL0NvbnRlbnRzIDUgMCBSL01lZGlhQ...
and PDF works, but when get it from URL i'm getting :
{"fileData":"JVBERi0xLjUNCjQgMCBvYmoNCjw8L1R5cGUgL1BhZ2UvUGFyZW50...
I checked it in notepad, the first answer and the content of "" are identical
any ideas what i can do?
I have an array of the object, each object contains some properties of type string and a file.
This is how my array looks like:
const args = [
{
name: 'presentation',
price: 9.99,
tags: 'invest, finance, business',
file: File,
thumbnail: File
},
{
name: 'presentation2',
price: 20.99,
tags: 'invest, finance, business',
file: File,
thumbnail: File
}
]
const headers = {
headers: {
Authorization: `Bearer ${token}`
}
};
My goal is to send this whole array of the object to the Express Server. there I will save information to the Mongo and upload the file to S3.
But Currently, I am unable to get the file stream on the server when I send this in a POST request,
even the whole req.body is an empty object when I console.log it
axios.post(`${slidesRoute}/createSlides`, args, headers);
Alternatively, I tried to put everything into FormData.
for example:
let formData = new FormData();
selectedFiles.forEach((selectedFile, i) => {
formData.append(`name_${i}`, selectedFile.name);
formData.append(`price_${i}`, selectedFile.price);
formData.append(`tags_${i}`, selectedFile.tags);
formData.append(`file_${i}`, selectedFile.file);
formData.append(`thumbnail_${i}`, selectedFile.thumbnail);
});
axios.post(`${slidesRoute}/createSlides`, formData, headers);
Then In the backend. I am unable to catch these files using multer. Because the filenames in form data are being generated dynamically like
file_1, file_2 file_3,...
But multer expects the exact filename to be passed
for example multer().array('file')
so in my second approach I am unable to catch files using Multer
You can't include file inside JSON. File is not JSON-serializable. Ideally you should have a separate endpoint to accept your files, send them with content-type: multipart/form-data, receive unique id for that file stored on the server, then send your JSON with ids instead of files.
As a chaper alternative you can Base64 encode your files into a string, insert this string in your request, and then decode it back on the receiving side
FormData key values are array by default
in your code Just change
formData.append(`file_${i}`, selectedFile.file);
to
formData.append(`file`, selectedFile.file);
You should be able to receive the file array in server side using multer().array('file')
Sample below demonstrating FormData key values array behavior
const formData = new FormData();
formData.append('key1', 'value1');
formData.append('key1', 'value2');
formData.append('key2', 'value1');
formData.forEach(function(value, key){
console.log(key, value);
});
I use FormData to send a number of files - and then on the node.js server I use Formidable.
I've pasted some partial code below that may help - sorry it's not a smaller sample.
Hope it helps
On the browser side I have:
let form_data = new FormData()
form_data.append('upload_data', file)
$.ajax({
url: '/file/upload' + remote_path + '/' + filename,
type: 'POST',
data: form_data,
processData: false,
contentType: false,
success: (res) => {
...
On the node.js side (apologies for the length..)I have:
const fs = require('fs')
const formidable = require('formidable')
const path = require('path')
...
app.post('/file/upload/*', (req, res) => {
let filename = req.params[0]
let media = path.basename(filename)
let folder = filename.substring(0, filename.length - media.length)
let form = new formidable.IncomingForm()
// form.encoding = 'utf-8'
form.multiples = true
form.uploadDir = path.join(media_folder, folder)
form.keepExtensions = true
form.parse(req, (err, fields, files) => {
if (err) {
fail(res, 'failed to upload')
} else {
success(res)
}
})
form.on('fileBegin', (name, file) => {
const [fileName, fileExt] = file.name.split('.')
file.path = path.join(form.uploadDir, `${fileName}_${new Date().getTime()}.${fileExt}`)
})
form.on('file', (field, file) => {
fs.rename(file.path, path.join(form.uploadDir, file.name), (err) => {
if (!res.headersSent) { // Fix since first response is received
fail(res, 'an error has occured with form upload' + err)
}
})
})
form.on('error', (err) => {
fail(res, 'an error has occured with form upload' + err)
})
form.on('aborted', (err) => {
fail(res, 'Upload cancelled by browser')
})
})
Steps:
I have a remote image url.
I generated the img tag from it.
Now I want to read this img tag and convert it into a File object so that I can send it to server for upload.
Method 1 Tried: I have already tried the fetch method and directly tried to fetch image data from remote url.
Method 2 Tried:
clicked = () => {
const img = document.getElementById('someId');
const options = {
method: 'get',
headers: {
'Access-Control-Request-Headers': '*',
'Access-Control-Request-Method': '*',
},
mode: 'cors',
};
fetch(img.src, options)
.then(res => res.blob())
.then((blob) => {
const file = new File([blob], 'dot.png', blob);
console.log(file);
});
}
Expected output is File object.
I am using axios here but the principal should be the same:
const res = await axios.get(`/api/image/${imageId}`, {
responseType: 'arraybuffer'
});
const imgFile = new Blob([res.data]);
const imgUrl = URL.createObjectURL(imgFile);
Ignore the response type.
I'm using a multi-step form in Vue.js to send multiple files to a Laravel backend. The form gives the user the ability to add multiple users to the database. Each user requires a file to be uploaded along with it.
Files are initially stored in state using Vuex. Each file is pushed to a files array in store.js
When the user submits the form, the files array looks as follows:
When the user submits the form I'm adding all the individual form data including the files array to a new FormData() object like so:
let fd = new FormData();
// append each file
for( var i = 0; i < store.state.files.length; i++ ){
let file = store.state.files[i];
fd.append('files[' + i + ']', file);
console.log(file);
}
// append rest of form data
fd.append('appointments', store.state.appointments);
fd.append('business_details', store.state.business_details);
fd.append('business_names', store.state.business_names);
fd.append('directors', store.state.directors);
fd.append('share_amount', store.state.share_amount);
fd.append('shareholders', store.state.shareholders);
Once all the form data is added I use Axios to send the form data to my Laravel backend.
axios.post('/businesses', fd, {
headers: {
'content-type': 'multipart/form-data'
}
})
.then(response => {
console.log(response);
this.completeButton = 'Completed';
})
.catch(error => {
console.log(error)
this.completeButton = 'Failed';
})
Inside my Laravel BusinessController I then want to Log::debug($_FILES) to see what files were sent along but all I get is an empty array.
[2018-10-05 16:18:55] local.DEBUG: array (
)
I've checked that the headers I'm sending includes 'multipart/form-data' and that I'm appending all my form data to new FormData() but I cannot figure out why the server receives an empty $_FILES array.
UPDATE 1:
If I Log::debug($request->all()); I get:
If I try to store the objects in that file like so:
foreach ($request->input('files') as $file) {
$filename = $file->store('files');
}
I get the following error:
[2018-10-06 09:20:40] local.ERROR: Call to a member function store() on string
Try this case:
var objectToFormData = function (obj, form, namespace) {
var fd = form || new FormData();
var formKey;
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (namespace) {
formKey = namespace + '[' + property + ']';
} else {
formKey = property;
}
// if the property is an object, but not a File,
// use recursivity.
if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
objectToFormData(obj[property], fd, property);
} else {
// if it's a string or a File object
fd.append(formKey, obj[property]);
}
}
}
return fd;
};
Reference : objectToFormData
Axios :
axios.post('/businesses', fd, {
transformRequest: [
function (data, headers) {
return objectToFormData(data);
}
]
})
.then(response => {
console.log(response);
this.completeButton = 'Completed';
})
.catch(error => {
console.log(error)
this.completeButton = 'Failed';
})
Try changing this line:
fd.append('files[' + i + ']', file);
To this:
fd.append('files[]', file);