uploading files to a quart based server - quart

I'm trying to convert my Flask server to Quart.
I have a form with file inputs in it, which I send over to the server. It used to work perfectly with Flask, but now I fail to send the files over.
Here's the code which worked:
app = Flask(__name__)
#app.route('/upload', methods=('POST',))
def process_form_data():
for name, file in request.files.items():
print(f'Processing {name}: {len(file.read())}')
return make_response(jsonify({"message": "File uploaded"}), 200)
The sending code is:
const request = new XMLHttpRequest();
function check_and_post(e) {
const formElement = e.target;
request.addEventListener("load", function (e) {
if (request.status == 200) {
window.location.href = '/work';
}
else {
alert('Error uploading file');
console.log(e);
}
});
// request error handler
request.addEventListener("error", function (e) {
alert('Error uploading files, try again.');
});
request.open("post", formElement.action);
request.responseType = "json";
request.send(new FormData(formElement));
}
My flask code consumed the files, and the load event was received with status 200.
My Quart code is as follows:
app = Quart(__name__)
#app.route('/upload', methods=('POST',))
async def process_form_data():
for name, file in (await request.files).items():
print(f'Processing {name}: {len(file.read())}')
return make_response(jsonify({"message": "File uploaded"}), 200)
Now I get to the "Error uploading file" branch, i.e. the request status received for the progress event isn't 200 (I didn't get any request for the error event).
The status of the request I now get in load is 413 (request too large?). The server side does not log any exception or warning.
Any idea?

just in case someone lese search for this:
Quart has a configurable property that let you to set the maximum size of your requests payload (MAX_CONTENT_LENGTH) to set that use the below command in
max_mb= 50
app.config['MAX_CONTENT_LENGTH'] = mb *1000 * 1024 # for 50 megabyte

Related

Dart: Processing an uploaded file on the server from client request

I am trying to process images uploaded from client on the server but am receiving the following errors. Am I processing the httpRequest wrong?
Unhandled exception:
Uncaught Error: HttpException: Connection closed while receiving data, uri = /api/upload
/// Client Code (dart file) (Works # sending request)
sendData(dynamic data) {
final req = new HttpRequest();
FormData fd = new FormData();
fd.append('uploadContent', data);
fd.appendBlob('uploadContent', data);
req.open("POST", "http://127.0.0.1:8080/api/upload", async: true);
req.send(fd);
req.onReadyStateChange.listen((Event e) {
if (req.readyState == HttpRequest.DONE &&
(req.status == 200 || req.status == 0)) {
window.alert("upload complete");
}
});
}
InputElement uploadInput = document.querySelector('#sideBar-profile-picture');
uploadInput.onChange.listen((Event event){
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
File file = files[0];
FileReader reader = new FileReader();
reader.onLoad.listen((e) {
print('results: ${reader.result}');
sendData(reader.result);
});
reader.readAsArrayBuffer(file);
}
});
I have a small server listening for the request (/api/upload) and calling handleUploadRequest with the httpRequest being passed in as the param.
Server code (This is where I am stuck)
Future<Null> handleUploadRequest(final HttpRequest httpRequest) async {
httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d)).then((builder) {
var data = builder.takeBytes();
print('bytes builder: ${data}');
});
}
I am trying to read the data so that I can store it on a cdn but never get a chance to since the connection always gets closed while receiving the data.
Any help on being able to complete this is appreciated. Been at this for the past couple days:/
It is hard to tell when/if you close the httpRequest. If you are doing it right after handleUploadRequest returns, it will indeed close the connection as you are not waiting for httpRequest.fold() to complete. Adding await as shown below and making sure to call httpRequest.close() after this function complete asynchronously should work
Future<Null> handleUploadRequest(final HttpRequest httpRequest) async {
await httpRequest.fold(new BytesBuilder(), (b, d) => b..add(d)).then((builder) {
var data = builder.takeBytes();
print('bytes builder: ${data}');
});
}
(FYI) I have a similar code that works when testing with curl and uploading a file

Uploading an Image file via ajax in add-on panel

I'm trying desperately to create a Firefox add-on that posts a file with the field name "Filedata" to a particular PHP script which will only work if it sees a JPG in the $_FILE["Filedata"] variable.
I put a web form with a file browser into panel.html, then I take the image and turn it into a canvas which I turn into a blob and send to main.js. I would be happy to send the file directly from panel.js, but nothing at all happens (no error message either) when I attempt to so.
In main.js, I have this code but I get an error message that FormData doesn't exist in main.js. What to do?
function ajupload(mydata) {
var fd = new FormData();
fd.append("Filedata", mydata);
const {XMLHttpRequest} = require("sdk/net/xhr");
var myrequest = new XMLHttpRequest();
myrequest.open('POST', 'MYSITE/image.php?action=upload');myrequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
myrequest.upload.addEventListener("progress", function(e) {
var percentage = Math.round((e.loaded * 100) / e.total);
}, false);
myrequest.onreadystatechange=function()
{
if (myrequest.readyState==4 && myrequest.status==200)
{
console.log("Response" + myrequest.responseText);
}
}
myrequest.send(fd);
}

Sequential Ajax Calls fail in ASP.NET MVC

I've looked at multiple solutions to this problem but nothing's working to fix my problem.
I'm using ASP.NET MVC 4.5.
Here are my steps:
Use ajax call in page to upload file.
Within same function that generates ajax call run an ajax call to refresh the page to include the uploaded file, after ajax call is finished.
I'm using this as the first call (to upload) (compliments of another Stack Overflow user):
function uploadFiles() {
document.getElementById('fileupload').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('uploadfilenames');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Dashboard/UploadFiles');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
//alert(xhr.responseText);
}
}
return false;
}
reloadMain();
}
The reloadMain() function is:
function reloadMain() {
$.ajax({
url: '/Dashboard/ThumbList/' + currentPath,
type: "GET",
timeout: 5000,
success: function (msg) {
$("#thumb-list").html(msg)
},
error: displayError("Unable to get file listing")
});
}
I have noticed this:
The 'refresh' doesn't include the uploaded file information in the response
IE11 and Chrome act differently.
It seems that the problem is that the controller/system doesn't complete the file operations soon enough (I saw a "denied access...file in use" error when using Chrome.
So, it would seem that the refresh ajax call needs to wait until the file system completes its work.
Would you agree? If so, how can I make this work?
You can either set your XMLHttpRequest async to false:
xhr.open('POST', '/Dashboard/UploadFiles', false);
Or you can call your refresh function in callback:
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Dashboard/UploadFiles');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
reloadMain(); //Only refresh after the file post get a 200 response
}
}

iOS 6 (iPhone/iPad) Image Upload "Request Body Stream Exhausted" with NTLM/Windows Authentication

I am working on trying to get iOS 6 to use XMLHttpRequest POSTs to upload images. This works on desktop and Android web browsers, but with iOS 6 I am getting an error on the page being posted to: "Request Body Stream Exhausted". (Using iOS Simulator with the Safari Web Inspector).
Here is the basic code of the page:
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}
function uploadFile() {
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "/UploadHandler.ashx");
xhr.send(fd);
}
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
document.getElementById('prog').value = percentComplete;
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
alert("The upload has been canceled by the user or the browser dropped the connection.");
}
When doing this on any other browser, the handler returns correctly and uploads the file. However, with iOS the ashx page has the error "request body stream exhausted".
Here is a screenshot of the inspector:
Any ideas?
UPDATE: This issue only occurs when NTLM/Windows authentication is enabled for the application in IIS. With forms or anonymous authentication, the upload works fine.
Thanks,
John
In iOS 6, Safari sends the file with the initial post, including the file. That means the file stream is at the end, or "exhausted."
However, with NTLM, it will get a 401 challenge in response, and then have to resend the post with the authentication information. Since it does not reset the file stream, it is unable to send the file again with the second post. You can see this in the IIS logs.
As far as I know, there is no particularly good way around it. I am changing my mobile app, so that it uses form authentication. I direct the mobile app to a separate login app on the same server, which is set to use Windows Authentication. The login app can then redirect back to the main app with a form authentication cookie, and all is well again.
You have to set the machine key on both apps in the web.config file, so that both are using the same keys for encryption and validation.
The code on the login app is as simple as
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
With HttpContext.Current.User.Identity
If .IsAuthenticated Then
Dim sUser As String = .Name.ToLower.Trim
FormsAuthentication.RedirectFromLoginPage(s, False)
End If
End With
End Sub
I solved the issue by not setting the self-defined HTTP Authenticate Head on iOS 7 and iOS 8. (At first, our service use the self-defined value for the Authenticate Head). And after the challenge being handled by the delegate, the request will have the "Authenticate: NTLMxxx automatically" header automatically. And the Put and POST works again.
This error Comes on IOS but you can use different approach
like change your code line in formdata where you appending the file
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
to the below line basically don't append the file control but use the base64 image data
var reader = new FileReader();
reader.readAsDataURL(opmlFile.files[0]);
reader.onload = function () {
var base64DataImg = reader.result;
base64DataImg = base64DataImg.replace('data:'imagetype';base64,', '');
}

two way communication between extension and content javascript files

i am trying to accomplish a two way communication request response in my firefox sidebar extension, i have a file named event.js this resides on the content side, i have another file called sidebar.js file which is residing in the xul. I am able to communicate from event.js to sidebar.js file using the dispatchEvent method. my event in turn raises a XMLHttpRequest in sidebar.js file which hits the server and sends back the response. Now, here i am unable to pass the response to the event.js file. I want the response to be accessed in the event.js file. Till now i have achieved only one way communication. Please help me in getting the two way communication.
Code is as follows:
// event.js file
// This event occurs on blur of the text box where i need to save the text into the server
function saveEvent() {
var element = document.getElementById("fetchData");
element.setAttribute("urlPath", "http://localhost:8080/event?Id=12");
element.setAttribute("jsonObj", convertToList);
element.setAttribute("methodType", "POST");
document.documentElement.appendChild(element);
var evt = document.createEvent("Events");
evt.initEvent("saveEvent", true, true);
element.dispatchEvent(evt);
//Fetching the response over here by adding the listener
document.addEventListener("dispatchedResponse", function (e) { MyExtension.responseListener(e); }, false, true);
}
var MyExtension = {
responseListener: function (evt) {
receivedResponse(evt.target.getAttribute("responseObject"));
}
}
function receivedResponse(event) {
alert('response: ' + event);
}
// sidebar.js file
window.addEventListener("load", function (event) {
var saveAjaxRequest = function (urlPath, jsonObj, methodType, evtTarget) {
var url = urlPath;
var request = Components.classes["#mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);
request.onload = function (aEvent) {
window.alert("Response Text: " + aEvent.target.responseText);
saveResponse = aEvent.target.responseText;
//here i am again trying to dispatch the response i got from the server back to the origin, but unable to pass it...
evtTarget.setAttribute("responseObject", saveResponse);
document.documentElement.appendChild(evtTarget);
var evt = document.createEvent("dispatchedRes"); // Error line "Operation is not supported" code: "9"
evt.initEvent("dispatchedResponse", true, false);
evtTarget.dispatchEvent(evt);
};
request.onerror = function (aEvent) {
window.alert("Error Status: " + aEvent.target.status);
};
//window.alert(methodType + " " + url);
request.open(methodType, url, true);
request.send(jsonObj);
};
this.onLoad = function () {
document.addEventListener("saveEvent", function (e) { MyExtension.saveListener(e); }, false, true);
}
var MyExtension =
{
saveListener: function (evt) {
saveAjaxRequest(evt.target.getAttribute("urlPath"), evt.target.getAttribute("jsonObj"), evt.target.getAttribute("methodType"), evt.originalTarget);
}
};
});
Why are you moving your fetchData element into the sidebar document? You should leave it where it is, otherwise your content code won't be able to receive the event. Also, use the content document to create the event. Finally, document.createEvent() parameter for custom events should be "Events". So the code after your //here i am again trying comment should look like:
evtTarget.setAttribute("responseObject", saveResponse);
var evt = evtTarget.ownerDocument.createEvent("Events");
evt.initEvent("dispatchedResponse", true, false);
evtTarget.dispatchEvent(evt);
Please note however that your code as you show it here is a huge security vulnerability - it allows any website to make any HTTP requests and get the result back, so it essentially disables same-origin policy. At the very least you need to check that the website talking to you is allowed to do it (e.g. it belongs to your server). But even then it stays a security risk because server response could be altered (e.g. by an attacker on a public WLAN) or your server could be hacked - and you would be giving an attacker access to sensitive data (for example he could trigger a request to mail.google.com and if the victim happens to be logged in he will be able to read all email data). So please make this less generic, only allow requests to some websites.

Resources