FormData & Axios : Send a file to a Node server - javascript

In Vuejs, I have a picture drag and drop code. I have tried, without success, to send the file to NodeJS using FormData and axios like many example in the web e.g.
export default function(url, file, name = "avatar") {
const formData = new FormData();
formData.append(name, file);
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
return axios.post(url, formData, config);
};
The issue come from the client side not from server
see https://github.com/expressjs/multer/issues/548
and
https://gist.github.com/HarshithaKP/ebc0e79800e5638fe827c157360378be
I also tried with a classic input and it works
<form ref='uploadForm'
id='uploadForm'
action='https://localhost:4000/api/upload/picture'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" />
<input type='submit' value='Upload!' />
</form>
I came to the conclusion : the header of Axios is incomplete.
Some people advice to complete the header by using the form-data library https://github.com/form-data/form-data
headers: {
...form.getHeaders(),
'Content-Type': 'multipart/form-data'
}
But in form-data, the library use for client the classical object FormData (https://developer.mozilla.org/fr/docs/Web/API/FormData) without the function getHeaders(). It doesn't exist. In the code, we have
module.exports = typeof self == 'object' ? self.FormData : window.FormData;
QUESTION
How can I populate properly the header in order that the server reconize the POST with a file ? A kind of a getHeaders() function.

Related

Vue formdata() for multiple objects to be added on database from django serializer

I want to add multiple objects to formdata so they can be added on the database at the same time.
This is my javascript code for sending the object
addImages(id) {
const formData = new FormData();
this.imageArray.forEach((element) => {
formData.append("image",element);
formData.append("product", id);
});
let config = {
headers: {
Authorization: `Bearer ${this.$store.state.accessToken}`,
"Content-Type": "multipart/form-data"
},
};
axios.post(`http://127.0.0.1:8000/images/`, formData, config,);
},
The problem is that in the backend the information comes like this :
<QueryDict: {'product': ['31', '31'], 'image': [<InMemoryUploadedFile: allstar.jpg
(image/jpeg)>, <InMemoryUploadedFile: cat.jpeg (image/jpeg)>]}>
And for it to be added to the database it should look similiar to this.
[{'product':'31','image': [<InMemoryUploadedFile: allstar.jpg (image/jpeg)]>},
{'product':'31','image':<InMemoryUploadedFile: cat.jpeg (image/jpeg)>]}>]
One solution that i came up with was to modify the data when i recived it in the backend
data_list =[]
new_data = dict(request.data)
for x,y in zip(new_data['image'],new_data['product']):
data_list.append({'image':x,'product':y})
I was wondering if there was a more cleaner solution that could be made when formdata was sent

Form submit value giving null - mongoose and express js

I am trying to make a way to send data from a basic html form to mongodb using express. but it's giving me null when I post it.
I used the following Schema : commentname: String.
Here's the HTML:
<form id="form" onsubmit="return false;">
<input type="textbox" id="cmt-1" placeholder="comment here"/>
</form>
<button type="button" onclick="submit()" class="submit">
submit
</button>
JS:
var cmt = document.getElementById('cmt-1').value;
var comment = {commentname: ''};
comment = {commentname: cmt};
function submit () {
async function postData (url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'same-origin',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData(
'https://realtime-comt-system-100.glitch.me/comments/add',{comment}
)
.then(data => { console.log(data); });
}
What am I missing?
just try this code but check the url i put . don't put the whole url just put the path name .
also take this code copy and paste , the html and the java script take them both because i changed both of them
var form = document.getElementById('form');
form.addEventListener('submit', async(e) => {
e.preventDefault();
let cmt = form.cmt.value;
let data = {commentname: cmt};
const response = await fetch('/comments/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(res =>{
console.log(res)
}).catch(err){
console.error(err)
}
})
<form id="form">
<input type="textbox" id="cmt-1" name='cmt' placeholder="comment here"/>
</form>
<button type="submit" onclick="submit()" class="submit">
submit
</button>
The problem is not being caused by the code posted above.
It's a Mongoose problem. You are trying to create two documents with the same id, when that id has been specified unique to Mongoose.
This is what the code is posting in the body to the backend, a JSON string:
{"comment":{"commentname":"my sample comment"}}
The fact that you're posting an object inside an object looks suspicious. This pattern would be more common:
{"commentname":"my sample comment"}
But since there is no backend code posted, it's impossible to tell if this is correct.
When I tried posting {"comment":{"commentname":"my sample comment"}} to the backend URL using Postman, I received the following response code:
400 Bad Request
The response body:
"Error: MongoError: E11000 duplicate key error collection: database.comments index: commentname_1 dup key: { commentname: null }"
From Mastering JS - Debug E11000 Errors in Mongoose
MongoDB's E11000 error is a common source of confusion. This error occurs when two documents have the same value for a field that's defined as unique in your Mongoose schema.
Mongoose models have an _id field that's always unique.

Passing multipart form data from python to django server with both file and data

I have a Django server and a react frontend application. I hava an endpoint that receives both data and file object. The javascript version of the api client is working fine and it looks something like this.
const address = `${baseAddress}/${endpoint}`;
const multipartOptions = {
headers: {
'Content-Type': 'multipart/form-data',
'Content-Disposition': `attachment; filename=${filename}`,
'X-CSRFToken': getCSRFToken(),
},
};
const formData = new FormData();
formData.append('file', file);
const json = JSON.stringify(metadata);
const blob = new Blob([json], {
type: 'application/json',
});
formData.append('metadata', blob);
return axios.post(address, formData, multipartOptions);
So as you can see I am using a blob to add metadata to my form data and passing it to the server.
Printing the request.data in the server gives me something like this.
<QueryDict: {'file': [<InMemoryUploadedFile: admin_12183.zip (application/zip)>], 'metadata': [<InMemoryUploadedFile: blob (application/json)>]}>
So I can access both request.data.get('file') and request.data.get('metadata') on my django server.
Now I have to do something similar in python. I tried using requests to get the stuff right, but I don't get two separate keys in the QueryDict. The python code looks like this.
with open("file.zip", "rb") as fp:
with open("metadata.json", "rb") as meta:
file_headers = {
**headers,
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryjzAXwA7GGcenPlPk',
}
data = {
"file": "",
}
files = {
'file': ("file.zip", fp, "application/zip"),
'metadata': ("blob", meta, "application/json"),
}
response = requests.post(f"{BASE_URL}/api/endpoint", data=data, files=files, headers=file_headers)
print(response.status_code)
If I do not send in both files and data at the same time, I get nothing in request.data. And if I send both of them, I am getting both of the data in a single key, that corresponds to whatever key I have in the data variable.
The server has this code in it
def post(self, request, *args, **kwargs):
file_obj = request.data.get('file')
metadata = request.data.get('metadata')
# both are empty if either one of files or data is not sent from the client
# if both are sent, then request.data has only one key, with everything inside of it
# works fine with the javascript code
I think I am missing something very small and trivial.
Please help.
Turns out I was adding a content-type in my header and that is what was causing all the issues. The working code looks like this
file_headers = {
**headers,
# make sure there is no content type in the header
}
files = {
'file': ("file.zip", shape, "application/zip"),
'metadata': ("blob", meta, "application/json"),
}
response = requests.post(f"{BASE_URL}/api/shapefiles", files=files, headers=file_headers)
print(response.text, response.status_code)

Sending files from client (angular) to server Asp.net wed api, request always has empty prorerty "files"

everyone, have a trouble with sending files
I have angularjs client:
var fd = new FormData();
var myEl = angular.element( document.querySelector( '#someId' ) );
fd.append('file', myEl[0].files[0]);
fd.append('test', 'value');
var vv = $resource('api/:domen/:url', { url: "", domen: 'Options' }, {
'saveF': {
headers: {
'Authorization': 'Bearer ' + currentUser.getProfile().token,
'Content-Type': false
},
method: 'POST'
}
});
vv.saveF({ url: "UploadData", domen: 'Info' }, fd, function (el) {
console.log(el);
});
The client code send fields, I can check it via Fiddler, the file is in body
and backend code on ASP.NET Web Api:
public async Task<IHttpActionResult> UploadData()
{
// var a = HttpContext.Current.Request;
// var f = a.Files.Get(0);
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
and so on
The request always fails, because of "Request.Content.IsMimeMultipartContent()" or var f = a.Files.Get(0);
and I cannot get the file, server never see a content in request, but can see content-length
Already try to white content type 'multipart/form-data' but id didn`t help
Please, if somebody knows how to fix it - answer
See back-end implementation of Web API 2: https://www.asp.net/web-api/overview/advanced/sending-html-form-data-part-2
Also, you can use ng-file-upload for AngularJS
And example: https://github.com/stewartm83/angular-fileupload-sample
If you upload unknown MIME type and host your application on IIS,
it would be good add MIME type to IIS.

Flask server cannot read file uploaded by POST request

I have my React client post a file with the fetch api to the '/dataset' endpoint.
import 'whatwg-fetch';
uploadData(csv) {
this.dataset = csv;
fetch('/dataset', {
method: 'POST',
body: this._fileToFormData(csv)
}).then(
(response) => {
console.log(response);
}
).catch( () => {} );
};
_fileToFormData(file) {
var formData = new FormData();
formData.append('file', file);
return formData
};
My Flask server is supposed to pick it up.
#app.route('/dataset', methods=['POST'])
def dataset():
print request.get_data()
csv_data = request.form['file']
print csv_data
return '{ "fake_json":100}', 200
However, the csv_data object is simply a unicode string, '[object File]'
The code
print "form:", request.form
print "files:", request.files
returns
ImmutableMultiDict([('file', u'[object File]')])
ImmutableMultiDict([])
How do I get the actual contents of the CSV file?
=== EDIT: Solved ===
the variable csv was actually a single file array, so I needed to extract the file out.
Uploaded files are available in request.files, not request.form. The values are file-like objects, so to get the data you need to read the file.
data = request.files['file'].read()
See the Flask docs for some examples on working with uploads.
You also need to upload the file correctly. GitHub's fetch polyfill has an example using FormData to format the body properly. You must pass a single file or input to each call to append.
var input = document.querySelector('input[type="file"]')
var data = new FormData()
data.append('file', input.files[0])
fetch('/dataset', {
method: 'POST',
body: data
})

Categories

Resources