Flask server cannot read file uploaded by POST request - javascript

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
})

Related

Create a file object of zip file based on a blob for formdata in a fetch

I would like to upload a zip file as part of formdata in a Javascript fetch.
I can easily send in a zip file which I have read in via a file input control (see first segment of code). But when try to create a file object based on a blob, I receive an error (see second segment of code).
Basically I am asking how to create a zip file object from a blob. I tried many variations of code, but cannot nail the right syntax of creating the file object from blob, but it has to be a zip file. Note that the zip file is a shapefile - not sure if it makes any difference.
//here we upload the zip file
var fileInput = document.getElementById('avatar');
var filename = fileInput.files[0].name;
//This works fine. I can easily send the zip file in formdata
var headers = new Headers();
headers.set('X-CSRFToken', csrftoken);
var formData = new FormData()
formData.set("time", false)
formData.set("base_file", fileInput.files[0] )
fetch("/upload/", {
"credentials": "include",
"body": formData,
"headers": headers,
"method": "POST",
"mode": "cors"
})
But when I try to upload zip file I created from a blob, then I receive an error
var shpBuffer = await ShapeFileFromGeoJSON(geojson, fileName);
var base64String = Uint8Array.from(window.atob(shpBuffer), (v) => v.charCodeAt(0));
var shapeFileBlob = new Blob([ base64String ], {type:"application/zip"});
saveAs(shapeFileBlob, fileName); //this works and a zip file is saved
var file = new File([shapeFileBlob], 'application.zip', { type: shapeFileBlob.type,});
console.log(file);
//Returns: "File {name: 'application.zip', lastModified: 1656002582746,
//lastModifiedDate: Fri Jun 24 2022 04:43:02 GMT+1200 (New Zealand Standard Time),
//webkitRelativePath: '', size: 6091726, …}"
console.log(file instanceof File); //returns true
var headers = new Headers();
headers.set('X-CSRFToken', csrftoken);
var formData = new FormData()
formData.set("time", false)
formData.set("file", file)
fetch("/upload/", {
"credentials": "include",
"body": formData,
"headers": headers,
"method": "POST",
"mode": "cors"
})
The error returned in the console is
POST https://WEBSITE/upload/ 400 (Bad Request)
{success: false, errors: Array(1)}
Yes, I think I solved the question. The issue was in the way I was creating the zip file - which is a shapefile. The shapefile zip file should unzip into the files directly without into any intermediate folder. Geoserver was rejecting it as upon unzipping it could not directly find any shapefile.

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

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)

Upload file to Django with Javascript

I have simple fetch function and I want to upload an base64 image. The function is as follows:
function upload_to_server(canvasData){
console.log(canvasData); // that is data:image/png;base64,iVBORw0KGgoAAAANSUh.......
return fetch(api_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: {photo: canvasData}
}).then(function (value) {
if(value.ok){
return value.json().then(function (response) {
debugger;
})
}
}).catch(function (reason) {
debugger;
})
}
And I have simple django view:
def upload_image(request):
print(request.POST)
pdb.set_trace()
It goes successful to that view when function upload_to_server gets called, but request.POST is empty. It shouldn't be empty, it should have key photo with that base64 value.
Any idea what I do wrong?
If someone else has the same problem. I solved it as follows.
I changed the body of fetch request to:
body: JSON.stringify({photo: canvasData})
and I changed django view, because data is in request.body and not in request.POST
import json
def upload_image(request):
body = json.loads(request.body)
photo = body['photo']
// Rest of the code
EXTRA
The photo is base64 encoded but I needed it as media file. I converted it to media file and saved it to database as follows:
def upload_image(request):
body = json.loads(request.body)
photo = body['photo']
img_format, img_str = photo.split(';base64,')
ext = img_format.split('/')[-1]
data = ContentFile(base64.b64decode(img_str), name='temp.' + ext) # You can save this as file instance.
try:
n_file = Files()
n_file.file = data
n_file.save()
return JsonResponse({'status': 'success'})
except Exception as err:
return JsonResponse({'status': 'failed'})

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.

Categories

Resources