angularjs+python(Flask) file upload to server - python

I am developing a web app using angular, python and Flask.
In my app there is a form where the user need to enter some data and to upload his photo.
I want to implement the file upload to the server using angular.
I saw that I need to use the FormData() and to bind the data from the HTML to angular using "watch".
The javascript part is clear.
I don't understand how can I get the data from the Python side.
This is my HTML -
<form enctype="multipart/form-data" ng-submit="submitGuideDetailsForm()">
<div class="form-group">
<label for="usr">Add your photo:</label>
<input type='file' class="form-control" name='file' onchange="angular.element(this).scope().uploadFile(this.files)">
</div>
</form>
This is my angular -
$scope.uploadFile = function(files) {
$scope.file = new FormData();
$scope.file.append("file", files[0]);
};
$scope.submitGuideDetailsForm= function() {
$http.post('/uploadFile', $scope.file, {
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success(function(results)
{
$log.log('success load file')
}).error(function(error)
{
$log.log('error load file!!!!!')
$log.log(error);
});
};
I want to get the file on the server side, using python and flask -
#app.route('/uploadFile', methods=['POST'])
def uploadFile():
json_data = request.json
print(json_data)
status = 'success'
return jsonify({'result': status})
I don't know how to get the data from the 'request' object.
request.json does not work.
Please advice me what am I doing wrong.
How can I get the data from the 'request' object?
Do I need to encode the data to a file?
How do I send the data back to client? Do I need to encode it back?
I did not find a full example using angular and python/flask uploading a file to server, saving it and than downloading it to the client.
Thanks a lot,
Dina

the file can be get byfile = request.files['file']
after that,
you can get the filename by file.filename.encode("utf8")

The command 'request.files' work
The gets me the following object -
ImmutableMultiDict([('file', <FileStorage:
u'12132638_10153196350425732_1151540092606190338_o.jpg'
('image/jpeg')>)])
I don't understand how can I save it on the server.
How do I get only the file name out of this object
And how do I send it back to the client side?
Thanks a lot, Dina

Use request.files and .filename atrribute
file = request.files['file']
fileName = file.filename
#to store it on server use getvalue() and write it to the file object
filedata = file.getvalue()
with open('abc.png') as f:
f.write(filedata)

Related

Uploading an excel file to FastAPI from a React app

Ok, I was really hoping that one of the various other questions on this topic would help me but I simply can't make this work! I'm relatively new to React and using API requests. I'm looking to upload an excel file from my React app and then process it in python using FastAPI as the interface.
I've followed the various tutorials / documentation approaches and I just get a 422 Unprocessable Entity Error!
In React, my event handlers look like this:
When file is selected, set the file in state:
onFileChange = (event) => {
this.setState({
uploadFile: event.target.files[0],
isFileSelected: true
});
};
When the "Upload" button is pressed, append to a FormData object and send via an axios request (Database is the axios object):
onUploadClick = async (event) => {
event.preventDefault()
var formData = new FormData();
formData.append(
"UploadFile",
this.state.uploadFile,
this.state.uploadFile.name
);
this.setState({isFileUploaded: true})
console.log(this.state.uploadFile)
const headers={'Content-Type': this.state.uploadFile.type}
await Database.post("/FileUpload",formData,headers);
};
And my FastAPI handler looks like this:
#app.post("/FileUpload")
async def FileUpload(file: UploadFile = File(...)):
# do some things...
Can someone please put me out my misery?
Your problem is due to the same reason as
How to send file to fastapi endpoint using postman
The name of the file variable in your fastapi endpoint has to match the name of the key to the file of your formdata. That is, your javascript should be like
formData.append(
"file",
this.state.uploadFile,
this.state.uploadFile.name
);
in order for your endpoint to be able to access the file parameter. Otherwise change the parameter's name (UploadFile in your case).

How to request several arguments from FormData?

This is a follow-up question from here: there I learned how to upload a file, process it and then populate the website again with its content using AJAX and FormData. For example, if I have a file data.csv like this:
A,B,C
1,3,4
2,4,2
I can pass it using AJAX and FormData
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div>
</form>
// the javascript part
var form = $('#fileUploadForm')[0];
var formdata = new FormData(form);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/_get_table",
data: formdata,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
and get:
I can then easily fetch this using
file = request.files['myfile']
and convert it into a dataframe using
df = pd.read_csv(file)
My question is now how I would do this, if I want to pass additional parameters (not only the file). Here it is suggested to use
var formdata = new FormData();
formdata.append("myform", form)
formdata.append("myvalue", 10)
which gives
and the headers
How do I now collect the info correctly? I can get myvalue like this
val = request.form['myvalue']
but I have not found a way to access and read myfile. If I e.g. try
file = request.files['myform']
I get
werkzeug.exceptions.HTTPException.wrap..newcls: 400 Bad
Request: KeyError: 'myform'
I also tried other solutions from here but without success.
The problem is because you didn't add your file to the FormData correctly.
As you are using multipart/form-data, your payload should be one file and some form-data. But in your screenshot, as you can see, you were adding one object.
To solve this problem, actually you can directly add your value to the original FormData:
var formdata = new FormData(form);
formdata.append("myvalue", 10);

Unable to generate a file to download with Django

Part which is working fine :
I have made a <form> whose submit call makes an ajax request.Code for the same :
$.ajax({
type: 'POST',
url: '/uploaded_proto_file/',
data: formdata,
processData: false,
contentType: false,
success: function (data) {
console.log("in success")
console.log("data is --->"+data)
return;
},
error: function (data) {
console.log("in error")
return;
}
});
I am able to receive the call for the same on the function below. Now i need to send a file (which is in my document structure) that needs to auto downloaded in my browser. I execute this function based on this answer
def uploaded_proto_file(request):
with open(
'/Users/metal/Documents/test_pb2.py','rb') as text_file:
response = HttpResponse(FileWrapper(text_file.getvalue()), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test_pb2.py'
return response
The above code wasn't working fine, so I debugged it and found this error on the filewrapper statement
Now I changed the solution a bit but the file sent back in response is not getting auto downloaded , it is getting printed in the console (as my success block in ajax function has console.log(data))
Changed solution :
with open(
'/Users/metal/Documents/test_pb2.py','rb') as text_file:
response = HttpResponse(text_file, content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=test_pb2.py'
return response
As you can see above I removed FileWrapper and was atleast able to send the file back to ajax request. But the problem still persists as the file is not getting auto downloaded.
P.S. : I have also tried opening the file in different modes like 'r','rb'.
Any help would be appreciated !
You can't return a file to an ajax request and expect it to be downloaded. What you need is to separate into a few steps:
Your ajax request triggers the file creation process
Django puts that file somewhere where it can be downloaded and returns the URL to the file as ajax response
Your js response handler can now do two things:
Generate a button in the HTML for the user to click and download the file (using the URL from the response)
Create the button and auto-click it (call .click() on it) and then delete it again. A bit like in this example

Django & Ajax: How can I create a csv file download from posting data through AJAX to Django?

I'm trying to allow the click trigger to do some backend dynamic csv creation, and then return it back to the user as a csv download file. I guess I'm not sure how I should write out the return statement other than just putting return response. I'v come across some other posts saying that I would need to set my url to a hidden iframe?? Not sure what this means though. Any tips?
Ajax looks like this:
$('#download-maxes').on('click', function(){
$.ajax({
type: "POST",
url: "{{request.path}}download/",
dataType: 'json',
async: false,
data: JSON.stringify(workouts),
success: function(workoutData) {
console.log(workoutData);
},
error:function(error){
console.log(error);
}
});
});
And my django view looks like this:
def download(request):
#(... a lot of mongo stuff here & other things defined)
workouts = json.load(request.body)
response = HttpResponse(content_type='text/xlsx')
response['Content-Disposition'] = 'attachment; filename="team_maxes.xlsx"'
writer = csv.writer(response)
writer.writerow(['Name', 'Date', 'Workout', 'Max'])
for member in team_members.all():
for wo in workouts:
wo_data = db.activity_types.find_one({"name": wo["name"]})
best_wo = db.activity.find_one({"u_id": member.user.id, "a_t": str(wo_data["_id"]), "is_last": 1}) or 0
member_name = member.user.first_name + ' ' + member.user.last_name
try:
max_stat = best_wo["y_ts"]
except:
max_stat = 0
try:
date = best_wo["e_d"]
except:
date = ""
workout_name = wo_data["name"]
writer.writerow([member_name, date, workout_name, max_stat])
return response
You don't need to use ajax. Since you are POSTing some json data to your view, just make a form with a hidden text input, and set it's value to the json data. Then make a regular submit button in the form.
When the form gets submitted, and the server responds with Content-Disposition: attachment; filename="team_maxes.xlsx", your browser will automatically trigger a download.
If you decide to go this route, Keep in mind:
You are using a regular html form now, with the POST method, so you must remember to use django's {% csrf_token %} tag inside of it.
You will probably have set the input's value to your json string, right before submitting the form.
Your workouts json gets sent in a form input. So, assuming you named your input "workouts", in your view you would do something like:
workouts = json.loads(request.POST.get('workouts'))
Plus a bunch of error checking, of course.

Save current html state to file via jQuery $.ajax POST to Flask view

Been at this for hours, giving up and posting in the hopes someone can help me out. Maybe this is a terribly stupid idea but I:
Have an html page that I modify using some jQuery controls (adding classes to a large list of items individually on click, filling in textareas, changing page title, etc..
On click of a save button want to do something like :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: myhtml
});
});
Then on the Python/Flask side:
#app.route('/parse_me', methods=['GET', 'POST'])
def parse_me():
if request.method == "POST":
#determine new filename based on #header id
#save new file with request.form data? request.something data??
#using the new filename
I read over the flask docs, and tried many many variations. I'm thinking my problem primarily lies in how I'm sending & then unpacking the data. The closest I got was using:
data: JSON.stringfy(myhtml)
and then on the python side I could "see" that data by doing
print request.values
inside the parse_me() method. Any help would be appreciated. It seemed like the "simplest" solution to what I want to accomplish: I modify the dom with jQuery then ship off my changes to flask to package them into a new file on the server.
Solved!
changed :
$('#save').click(function() {
myhtml = $('html').html();
$.ajax({
type: "POST",
url: "http://127.0.0.1:5000/parse_data",
data: {html:myhtml} // made into a hash for retrieval
});
});
on the python side :
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data():
# updated: get value for key "html", encode correctly
data = request.form.get("html").encode("utf-8")
# do whatever with the data
return "Data retrieved"
Very easy.. loving Flask!

Categories

Resources