Can't upload image to a external API using axios - laravel

I've been trying to upload an image with axios to an external (laravel) api and it's been giving me nightmares.
resumed template:
<v-form>
<v-file-input
label="Logo*"
v-model="image"
accept="image/*"
#change="onFileSelected"
required
></v-file-input>
<v-btn color="blue darken-1" text #click="createProvider">Create Provider</v-btn>
</v-form>
Methods
methods: {
onFileSelected (event) {
this.selectedImage = event;
},
createProvider() {
let formData = new FormData();
formData.append("image", this.selectedImage, this.selectedImage.name);
const config = {
headers: {
Authorization: this.token,
'content-type': 'multipart/form-data'
}
};
let imageData = {
'image': formData,
'name': 'Provider Image', // Required Field
}
axios.post('http://fake_external_url.com/api/images', imageData, config) // laravel API
.then(console.log)
.catch(console.log)
},
}
The error that I get in return is:
Error: Request failed with status code 422
Request Response:
[HTTP/1.1 422 Unprocessable Entity 1374ms]
{"image":{},"name":"Image Provider"}
I see that the image is not receiving anything.
If I console.log this.selectedImage I get:
File {
name: "happy.jpg",
lastModified: 1596711013544,
webkitRelativePath: "",
size: 41292,
type: "image/jpeg"
}
If I console.log FormData I get crap
FormData
​<prototype>: FormDataPrototype
​​append: function append()
​​constructor: function ()
​​delete: function delete()
​​entries: function entries()
​​forEach: function forEach()
​​get: function get()
​​getAll: function getAll()
​​has: function has()
​​keys: function keys()
​​set: function set()
​​values: function values()
​​Symbol(Symbol.toStringTag): "FormData"
​​Symbol(Symbol.iterator): function entries()
​​<prototype>: Object { … }
My environment: localhost on a XAMPP server (php artisan serve as well). Laravel, VueJS, Vuetify latest versions.
I think my problem likes in my FormData, but it may be from the variables that it's receiving from the event click. I am out of ideas.
[EDIT] Note: I am able to upload image when using POSTMAN.
The reason why I am using event, and not the classic event.target.files[0] it's because there is no target in the response from the console.log.

You have two problems.
FormData
You need to send a FormData object, only a FormData object, and nothing but a FormData object.
If you want to pass additional data, then append it to the FormData object.
Wrapping the FormData object in another object and passing it to (for example) a JSON serializer will just break it.
Content-Type
The multipart/form-data MIME type has a mandatory boundary parameter.
You have omitted it, but you can't know what it is anyway.
Do not set the Content-Type header manually. The underlying XHR object will read it from the FormData object.

Related

NativeScript Vue send request with form data (multipart/form-data)

I have a case in my application where I need to send data as form data to a server. The data includes a message and an optional list of files. The problem I'm facing is that when sending the request it's not being formed properly.
Request Payload
Expected (sample with the same request in the browser)
Actual (resulting request when running in NativeScript)
The actual result is that the payload is somehow being URL encoded.
Code example
sendData({ id, message, files }) {
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
const payload = new FormData();
payload.append('message', message);
if (files && files.length > 0) {
files.forEach((file) => {
payload.append(`files`, file, file.name);
});
}
return AXIOS_INSTANCE.post(
`/api/save/${id}`,
payload,
config
);
}
As you can see from the above, I'm using axios and also I'm trying to use FormData to format the data. From my research it seems that NativeScript used to not support binary data via XHR - however looking at this merge request on GitHub it looks like it's been fixed about a year ago.
So my suspicion is that I'm doing something wrong, maybe there's an alternative to using FormData, or else I shouldn't use axios for this particular request?
Version Numbers
nativescript 6.8.0
tns-android 6.5.3
tns-ios 6.5.3
Nativescript's background-http supports multipart form data.
See below for how its configured to do multipart upload
var bghttp = require("nativescript-background-http");
var session = bghttp.session("image-upload");
var request = {
url: url,
method: "POST",
headers: {
"Content-Type": "application/octet-stream"
},
description: "Uploading "
};
var params = [
{ name: "test", value: "value" },
{ name: "fileToUpload", filename: file, mimeType: "image/jpeg" }
];
var task = session.multipartUpload(params, request);

Sending Files From Vuex to Backend using Axios

I have a vuex which stores few informations and also the files uploaded by the users. Now I am trying to send all the vuex data to backend using axios to process it but for some reason the file array is empty.
But when I see in VueDevTools, there is a file object in the vuex state.
This is my axios call, it is just not images which I am trying to send.
axios.post('/admin/form/saveForm',{
data : this.$store.getters.getEditCollector,
formData: this.$store.getters.getCollector,
formName : this.formName,
task: this.task,
id: this.formEntryId,
formId: this.formId,
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {})
.catch(error => {});
My Vuex is below snapshot.
As we can see, the logo state has a File Object, but when the request is received in the backend( PHP- Laravel), It is an empty array.
What is wrong here? Can someone point out the mistake?
EDIT1: Added Network Payload
EDIT2: Added Backend Code - Laravel
public function saveForm(Request $request){
// form data here has logo but empty array <------
$formData = $request->input('data');
$wholeFormData = $request->input('formData');
$this->formID = $request->input('formId');
....
...
}

Post MultipartFile - Request Part not preset Error

I try to send an image from a ionic Front-End application through a post method to Back-End services in Spring boot
I have done this method that makes the post to the backend url with the image inside a FormData object:
uploadImageService(url: string, image: any) {
console.log('post service: upload Image', + url);
// Initiates a FormData object to be sent to the server
const fd: FormData = new FormData();
fd.append('file', image);
const xhr = new XMLHttpRequest;
console.log('form data file: \n' + fd.get('file'));
xhr.open('POST', url);
// Send the FormData
xhr.send(fd);
console.log(xhr.response);
return xhr.responseText;
}
// call this method:
this.webapiService.uploadImageService(this.globalDataService.getUrlMedium() 'riskcontrol/subir-imagen', this.selectedImage);
This is the spring boot method that collects this post:
#RequestMapping(method = RequestMethod.POST, value = "/subir-imagen")
public ResponseEntity handleFileUpload(#RequestParam("file") MultipartFile file) {
LOGGER.log(Level.INFO, "/Post, handleFileUpload", file);
String associatedFileURL = fileManagerService.storageFile(file);
return ResponseEntity.ok(associatedFileURL);
}
When I do the post of the image I get this error:
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
I have launched the petition through Postman and it has worked,
that's why I think the error is in the tyscript code.
The only difference I see between postman and the code, is that in the form-data, let mark the key as type file or type text, and I have chosen type file.
I tried to make the request post in another way:
const httpOptionsImages = {
headers: new HttpHeaders({
'Content-Type': 'multipart/form-data',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, PUT, POST, DELETE'
})
};
// function
uploadImageService(url: string, image: any): Observable<any> {
console.log('post service: upload Image', + url);
// Initiates a FormData object to be sent to the server
const fd: FormData = new FormData();
fd.append('file', image);
return this.http
.post(url, fd, httpOptionsImages);
}
// call to the function
this.webapiService.uploadImageService(this.globalDataService.getUrlMedium() + 'riskcontrol/subir-imagen', this.selectedImage)
.subscribe( result => {
console.log(result);
});
But in this way I got another error:
FileUploadException: the request was rejected because no multipart boundary was found
What am I doing wrong?
Is there any way to indicate to FormData that the key is of type file as in postman?
Add the image as a Blob follow Ionic tutorial
const imgBlob = new Blob([reader.result], {type: file.type});
formData.append('file', imgBlob, file.name);
In the readFile function the program utilizes the FileReader from the File API to read the file into an ArrayBuffer. The onloadend event is called as soon as the file is successfully read. The app then creates a FormData object, wraps the array buffer in a Blob and adds it to the FormData object with the name 'file'. This is the same name the server expects as request parameter.
Remove your 'Content-Type': 'multipart/form-data'.
Have you tried on using #RequestPart instead of #RequestParam for MultipartFile file.

I can't use json to make a Post request to my web api using react

I created a webapi in ASP.NET Core, and I need to consume it using React, the web api works normally, if I use curl or postman among others, it works normally. The problem starts when I'm going to use React, when I try to make any requests for my API with js from the problem.
To complicate matters further, when I make the request for other APIs it works normally, this led me to believe that the problem was in my API, but as I said it works with others only with the react that it does not. I've tried it in many ways.
The API is running on an IIS on my local network
Attempted Ways
Using Ajax
$ .ajax ({
method: "POST",
url: 'http://192.168.0.19:5200/api/token',
beforeSend: function (xhr) {
xhr.setRequestHeader ("Content-type", "application / json");
},
date: {
name: 'name',
password: 'password'
},
success: function (message) {
console.log (message);
},
error: function (error) {
/ * if (error.responseJSON.modelState)
showValidationMessages (error.responseJSON.modelState); * /
console.log (error);
}
});
Using Fetch
const headers = new Headers ();
headers.append ('Content-Type', 'application / json');
const options = {
method: 'POST',
headers,
body: JSON.stringify (login),
mode: 'cors' // I tried with cors and no-cors
}
const request = new Request ('http://192.168.0.19:5200/api/token', options);
const response = await fetch (request);
const status = await response.status;
console.log (response); * /
// POST adds a random id to the object sent
fetch ('http://192.168.0.19:5200/api/token', {
method: 'POST',
body: JSON.stringify ({
name: 'name',
password: 'password'
}),
headers: {
"Content-type": "application / json; charset = UTF-8"
},
credentials: 'same-origin'
})
.then (response => response.json ())
.then (json => console.log (json))
Using Request
var request = new XMLHttpRequest ();
request.open ('POST', 'http://192.168.0.19:5200/api/token', true);
request.setRequestHeader ('Content-Type', 'application / json; charset = UTF-8');
request.send (login);
ERRORS
Console
Network tab
When I do this without being change the content type to JSON it works
because the API returns saying that it is not a valid type.
Apart from allowing CORS in you .NET configuration. You also need to return 200 OK for all OPTION requests.
Not sure how it's done in .NET but just create a middleware that detects the METHOD of the request, and if it's OPTIONS, the finish the request right there with 200 status.
Well I had the same issue and it seems that you need to add the action to the HttpPost attribute in the controller.
Here is an example.
[HttpPost("[action]")]
public void SubmitTransaction([FromBody] SubmitTransactionIn request)
{
Ok();
}
Try like this
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(option => option.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
app.UseAuthentication();
app.UseMvc();
}

POST binary data from browser to JFrog / Artifactory server without using form-data

So we get a file (an image file) in the front-end like so:
//html
<input type="file" ng-change="onFileChange">
//javascript
$scope.onFileChange = function (e) {
e.preventDefault();
let file = e.target.files[0];
// I presume this is just a binary file
// I want to HTTP Post this file to a server
// without using form-data
};
What I want to know is - is there a way to POST this file to a server, without including the file as form-data? The problem is that the server I am send a HTTP POST request to, doesn't really know how to store form-data when it receives a request.
I believe this is the right way to do it, but I am not sure.
fetch('www.example.net', { // Your POST endpoint
method: 'POST',
headers: {
"Content-Type": "image/jpeg"
},
body: e.target.files[0] // the file
})
.then(
response => response.json() // if the response is a JSON object
)
You can directly attach the file to the request body. Artifactory doesn't support form uploads (and it doesn't look like they plan to)
You'll still need to proxy the request somehow to avoid CORS issues, and if you're using user credentials, you should be cautious in how you treat them. Also, you could use a library like http-proxy-middleware to avoid having to write/test/maintain the proxy logic.
<input id="file-upload" type="file" />
<script>
function upload(data) {
var file = document.getElementById('file-upload').files[0];
var xhr = new XMLHttpRequest();
xhr.open('PUT', 'https://example.com/artifactory-proxy-avoiding-cors');
xhr.send(file);
}
</script>
Our front-end could not HTTP POST directly to the JFrog/Artifactory server. So we ended up using a Node.js server as a proxy, which is not very ideal.
Front-end:
// in an AngularJS controller:
$scope.onAcqImageFileChange = function (e) {
e.preventDefault();
let file = e.target.files[0];
$scope.acqImageFile = file;
};
// in an AngularJS service
createNewAcqImage: function(options) {
let file = options.file;
return $http({
method: 'POST',
url: '/proxy/image',
data: file,
headers: {
'Content-Type': 'image/jpeg'
}
})
},
Back-end:
const express = require('express');
const router = express.Router();
router.post('/image', function (req, res, next) {
const filename = uuid.v4();
const proxy = http.request({
method: 'PUT',
hostname: 'engci-maven.nabisco.com',
path: `/artifactory/cdt-repo/folder/${filename}`,
headers: {
'Authorization': 'Basic ' + Buffer.from('cdt-deployer:foobar').toString('base64'),
}
}, function(resp){
resp.pipe(res).once('error', next);
});
req.pipe(proxy).once('error', next);
});
module.exports = router;
not that we had to use a PUT request to send an image to Artifactory, not POST, something to do with Artifactory (the engci-maven.nabisco.com server is an Artifactory server). As I recall, I got CORS issues when trying to post directly from our front-end to the other server, so we had to use our server as a proxy, which is something I'd rather avoid, but oh well for now.

Resources