Implementing an Image/File-Upload into my aurelia App. The Express Api works and successfully receives Files and Images using Multer Plugin, tested with Postman.
Now I'm trying to figure out, how to attach the files ot the Aurelia Http/Fetch-Call. Somehow the request is made to the Api, but the images seem not attached. Any Ideas?
Some questions:
Is it correct to attach - in whatever way - the files to the body-key of the request, or do the files need to go seperately?
Is it correct/neccessary to create FormData and to attach the files - in whatever way - to that Obejct?
In what part of the HTTP-Request are the files/images stored, how to identify them serverside (in case my middleware is not working correctly)?
This is my approach so far:
Aurelia-Service:
uploadImagesApi(images) {
let formData = new FormData()
formData.images = images;
return http.fetch(url+"/", {
method: 'put',
body: formData, // <-- Is this correct?
}).then(response => {
return response
}).catch(error => {
console.log("Some Failure...");
throw error.content;
})
}
Input-Element
<input class="input" type="file" files.bind="images" multiple>
<a click.delegate="uploadImagesApi(images)">Upload</a>
uploadImagesApi(images) {
let formData = new FormData()
for (let i = 0; i < images.length; i++) {
formData.append('images', images[i]);
}
return http.fetch(url + "/", {
method: 'put',
body: formData,
})
.then(response => {
return response
})
.catch(error => {
console.log("Some Failure...");
throw error.content;
})
}
make sure your url variable is defined looks like a global
Change formData.images = images; to formData.images = images.item(0);
It might also be required to use 'file' key in formData instead of 'images': formData.file = images.item(o)
Related
I'm trying to get my head around the Nuxt /server API and can't seem to figure out how to send a POST request with form-data (ie files) to Nuxt server to forward on to an external service:
In my pages.vue file I have this method:
async function onSubmit() {
const formData = new FormData();
for (let file of form.files) {
await formData.append("image", file);
}
await $fetch("/api/send", {
method: "POST",
body: formData
});
}
and then in /server/api/send.js I have:
export default defineEventHandler(async (event) => {
const { method } = event.node.req;
// I THINK THE ISSUE IS HERE
const body =
method !== "GET" && method !== "HEAD"
? await readMultipartFormData(event)
: undefined;
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: body
});
return response._data;
}
I'm effectively creating a passthrough API using Nuxt so that the external endpoint isn't exposed to the end user. Just can't figure out how to access the formData in the correct format to pass through on the server side. I don't think I am supposed to use readMultipartFormData() because that seems to be parsing the data somehow whereas I just want to pass the formData straight through to the external API. Any tips?
I've tried using both readMultipartFormData() and readBody() and neither seem to work. I don't actually need to read the body but rather get it and pass it through without any formatting...
If you want to pass the data with formdata to the endpoint try this library:
https://www.npmjs.com/package/object-to-formdata
code:
import { serialize } from 'object-to-formdata';
const formData = serialize(body);
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: formData
});
I managed to make it work with ugly solution, first you have to update nuxt to version 3.2.0 min then here my front side
let jobApplicationDTO = {
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
phoneNumber: values.phoneNumber,
company: values.company,
shortDescription: values.shortDescription
};
const formData = new FormData();
formData.append("application", new Blob([JSON.stringify(jobApplicationDTO)], {type: "application/json"}));
formData.append("file", values.file) ;
//formData.append("file", values.file );
await useFetch("/api/application", {
method: "POST",
body: formData,
onResponse({request, response, options}) {
// Process the response data
if (response.status === 200) {
errorMessage.value = "";
successMessage.value = "Your application wa sent successfully, you will be contacted soon !";
}
},
onResponseError({request, response, options}) {
console.debug(response);
if (response.status === 400) {
successMessage.value = "";
errorMessage.value = "There may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
} else {
successMessage.value = "";
errorMessage.value = "Sorry we couldn’t send the message, there may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
}
},
});
}
and server side
import {FormData} from "node-fetch-native";
export default defineEventHandler(async (event) => {
const {BACKEND_REST_API, ENQUIRY_TOKEN} = useRuntimeConfig();
//retrieve frontend post formData
const form = await readMultipartFormData(event);
const applicationUrl = BACKEND_REST_API + '/job/apply'
console.log("url used for enquiry rest call :" + applicationUrl);
console.log("Job application token :" + ENQUIRY_TOKEN);
const formData = new FormData();
console.log(form);
if (form) {
formData.append(form[0].name, new Blob([JSON.stringify(JSON.parse(form[0].data))], {type: form[0].type}));
formData.append(form[1].name, new Blob([form[1].data], {type: form[1].type}), form[1].filename);
}
console.log(formData.values);
return await $fetch(applicationUrl, {
method: "POST",
body: formData,
headers: {
Authorization: ENQUIRY_TOKEN,
},
});
})
What is funny is on frontend you have to create a formData , then to get content and to recreate a formData from your previous formData converted in MultiFormPart[], i created a ticket on nuxt to see how to do it properly
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
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.
I want to take a picture, save it to an album and send the image right away.
(Do not select from album.)
There is no problem in taking the current picture, saving it in an album and checking the uri.
but the x-www-form-urlencoded sending process does not work properly.
I think there may be a problem with the format of sending the API.
postman is also attached. (No problem in Postman.)
takePictureAndCreateAlbum = async () => {
const { uri } = await this.camera.takePictureAsync();
console.log('uri', uri);
const asset = await MediaLibrary.createAssetAsync(uri);
console.log('asset', asset);
const filename = asset.filename;
MediaLibrary.createAlbumAsync('Expo', asset)
// POST API
.then(() => {
var file = new FormData();
file.append({file: uri});
return fetch(/* API_URL */, {
method: 'POST',
headers:{
'Content-Type':'application/x-www-form-urlencoded',
'Accept': 'application/json'
} , body : file} )
.then((response) => response.text())
.then((responseData) => {
console.log(responseData);
})
.done();
})
.catch(error => {
Alert.alert('An Error Occurred!')
});
};
Postman Header
Postman Body
When appending to a FormData instance, the first parameter is the field name (What your server will be getting as an object key for this file), the second one is an object in the following form:
{
uri: // The uri you received from the camera,
type: // The file type (i.e.: image/png),
name: // The file name
}
In your case, it would be like this:
// Split the uri by `.` (periods)
const uriParts = uri.split('.');
// Grab the file type at the end of the uri
const fileType = uriParts[uriParts.length - 1];
file.append('picture', {
uri,
type: `image/${fileType}`,
name: 'FILE-NAME.${fileType}`
});
Also, when sending files, your Content-Type header should be multipart/form-data
Hi am new to aurelia js , i need to upload file to server,am using autrelia js, materializecss and httpClient.fetch for api call. I dont'know how to send file to server.
view :
<input type="file" files.bind="selectedFiles" change.delegate="onSelectFile($event)">
Model :
onSelectFile(e)
{
var myurl = 'http://cdn.dmsapp.tk/file?authToken=bLNYMtfbHntfloXBuGlSPueilaHtZx&type=jpg&name=sibi.jpg&userId=7&organizationId=1&sourceType=USER_UPLOADS';
this.httpValueConverter.call_http(myurl,'POST',this.selectedFiles[],'fileupload',file_upload)
.then(data => {
console.log(data);
if(data.meta && data.meta.statusCode == 200) {
// this.index_lists = data.index.list;
}
}); }
httpservice :
return this.httpClient.fetch('http://api.dmsapp.tk/'+url,
{
method: method,
body : json(myPostData),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'authorization': this.authorization}})
.then(response => response.json());
looking for a solution.
If it's a file and you are trying to upload a particular media type,
the header 'Content-Type': 'application/x-www-form-urlencoded' does not seem right to me. Have a look at the appropriate media type here:
http://www.iana.org/assignments/media-types/media-types.xhtml
Also, you serialize data to JSON, if your data is binary you will need to change that to a byte array.
You might find some useful info here:
http://www.petermorlion.com/file-upload-with-aurelia/
Also you set a token both in your URL and your header, I'd recommend to set it in the header only.