I have this simple html:
<input class="input" type="file" name="file" id="imageInp"></input>
<button class="addBtn" id="addBtn">Add</button>
And this JS:
var HttpClient = function () {
this.get = function (aUrl, img) {
var anHttpRequest = new XMLHttpRequest();
var fdata = new FormData();
fdata.append("file", img);
anHttpRequest.open("POST", aUrl, true);
anHttpRequest.setRequestHeader("Content-type", "multipart/form-data");
anHttpRequest.send(fdata);
}
}
document.getElementById("addBtn").onclick = function() {
image = document.getElementById("imageInp").files[0];
var client = new HttpClient();
client.get(*url_here*, image);
}
And this code in flask:
#app.route('*url_here*', methods=['POST'])
def foo():
try:
print(request.form)
print(request.files)
print(request.data)
frame = request.files['file'].read()
return 'OK', 200, {'ContentType':'application/json'}
except Exception as e:
log('Main', e)
return 'Error', 400, {'ContentType':'application/json'}
And when I use that all I see this:
ImmutableMultiDict([])
ImmutableMultiDict([])
b''
400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
So, flask doesn't see my file. How can I fix it? Thanks in advance.
Found solution here: Upload files to Flask server via xhr
It's strange. I saw another questions where people recommended using this header.
Related
Pretty much what the title says. Here's the javascript... Works fine when not validating the token. Doesn't appear to see it when validating as I get The required anti-forgery form field "__RequestVerificationToken" is not present. error.
var downloadEmailSignature = function (control) {
if (control) {
let form = $(control).closest('form');
let token = $(form).find('input[name="__RequestVerificationToken"]').val();
if (form) {
let request = new XMLHttpRequest();
request.open("POST", "Forms/DownloadEmailSignature");
request.responseType = "blob";
request.setRequestHeader('RequestVerificationToken', token);
request.data = form.serialize();
request.onload = function () {
if (window.clientData.browser.name === "Internet Explorer") {
window.navigator.msSaveBlob(this.response, "EmailSignature.hta");
}
else{
let url = window.URL.createObjectURL(this.response);
let a = document.createElement("a");
document.body.appendChild(a);
a.href = url;
a.download = this.response.name || "download-" + $.now();
a.click();
}
};
console.dir(request);
request.send();
}
}
};
and the code behind...
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DownloadEmailSignature(string emailSignature)
{
var hta = (MediaItem)SitecoreContext.GetItem<Item>(new Guid("{EE806F14-5BD3-491C-9169-DA701357FB45}"));
using (var reader = new StreamReader(MediaManager.GetMedia(hta).GetStream().Stream))
{
var htaText = reader.ReadToEnd();
htaText = htaText.Replace("*CARDSTRING*", emailSignature);
var stream = new MemoryStream(htaText.ToASCIIByteArray());
return new FileStreamResult(stream, "application/hta");
}
}
And finally the view...
<form id="download-email-signature" method="POST" enctype="multipart/form-data">
#Html.HiddenFor(m => m.EmailSignatureMarkup)
#Html.AntiForgeryToken()
#Html.FormIdentifier("FormsController", "DownloadEmailSignature")
Download installer
</form>
If you can replace XMLHttpRequest with $.ajax (as you already have JQuery loaded), the below segment should work.
let form = $(control).closest('form');
let token = $(form).find('input[name="__RequestVerificationToken"]').val();
$.ajax({
url: '/Home/DownloadEmailSignature',
type: 'POST',
data: {
__RequestVerificationToken: token,
emailSignature: 'emailSignature value'
},
success: function (result) {
alert(result);
//your code ....
}
});
According to the answer here:
ValidateAntiForgeryToken purpose, explanation and example
MVC's anti-forgery support writes a unique value to an HTTP-only
cookie and then the same value is written to the form. When the page
is submitted, an error is raised if the cookie value doesn't match the
form value.
Which implies the cookie MUST go back to the server. This should be working fine unless you are using IE9 (which is known to be problematic with this issue).
I recommend you include request.withCredentials = true; to eliminate any strange CORs related issue.
If a proxy is involved, the cookie may be getting stripped on the way back to the server too.
This question already has answers here:
Post values from an HTML form and access them in a Flask view
(2 answers)
Closed 4 years ago.
I am trying to add new feature to my web application, to make file uploads avaliable.
Tech: Flask, Python 3. I am using ajax an object FormData, but it doesn't work.
Can’t send file to server.
Function ajax() in javascript causes error 400.I have tried to add a form in html and to use FormData(form). But it didn't change anything.
html
<label for="new-homework" class="add">Add</label>
<button class="send">Submit</button>
<input type="file" style="visibility:hidden;" multiple id="new-homework" value="Choose files">
javascript
function ajax(type, url, data, callback) {
var f = callback || function (data) {};
var request = new XMLHttpRequest();
request.onreadystatecheng = function () {
if(request.readyState == 4 && request.status == 200){
console.log('Success');
}
}
request.open(type, url);
if(type == 'POST')
request.setRequestHeader('Content-type','application/json; charset=utf-8');
console.log(data);
request.send(data); // Issue is here
}
var nf_button = document.getElementById("new-homework");
nf_button.addEventListener("change", function() {
console.log('working');
var files = nf_button.files;
canSendFiles(files);
}, false);
function canSendFiles(files) {
document.querySelector('button.send').onclick = function () {
var form = new FormData();
form.append('homework', files);
console.log(form.getAll('homework'));
ajax('POST', '/upload_file', form, function () {
console.log('Sent');
});
}
}
python
UPLOAD_FOLDER = "C:\chess_school\pa\saved"
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif',
'TXT', 'PDF', 'PNG', 'JPG', 'JPEG', 'GIF']
)
#app.route('/upload_file', methods=['GET', 'POST'])
def upload_file():
"""
Files upload
:return:
"""
if request.method == 'POST':
file = request.files['file']
print(file)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
path = app.config['UPLOAD_FOLDER']
if not os.path.exists(path):
os.makedirs(path)
file.save(os.path.join(path, filename))
return redirect(url_for('uploaded_file',
filename=filename))
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
'''
#app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
What's wrong? How to fix it?
Any help will be appreciated!
I am not quite sure as I'm a newbie as well, but I have a feel like you are missing the name in your HTML.
HTML
<input type="file" style="visibility:hidden;" multiple id="new-homework" value="Choose files" name="file">
Python
if request.method == 'POST':
file = request.files['file']
print(file)
Thanks.
I'm following a tut from here but I can't get the ajax to work, also I did checked the headers in chromes console, but I didn't see a text/json header as well! Could you please help me with it I can't figure it out.
Thanks in advance.
main.py
class Main(webapp2.RequestHandler):
def get(self):
if self.request.get('fmt') == 'json':
data = {'name' : 'sam', 'age': 25}
self.response.headers['content-type'] = 'text/json'
self.response.write(json.dumps(data))
return
self.templateValues = {}
self.templateValues['title'] = 'AJAX JSON'
template = jinja_environment.get_template('index.html')
self.response.write(template.render(self.templateValues))
app = webapp2.WSGIApplication([('/.*', Main),], debug=True)
index.html
<input type ="button" id="getitbutton" value="click button"/>
<div id= "result">
</div>
js script
<script type="text/javascript" >
function showData(data){
console.log(data.name);
$('#result').html(data.name)
}
function handleclick(e){
$.ajax('/',{
type: 'GET',
data: {
fmt: 'json'
}
success: showData
});
}
$(document).ready(function(){
$('#getitbutton').on('click', handleclick);
});
</script>
There are some problems with the current browsers, especially IE which does not send the text/json content type header. So I learned not to depend on the headers.
Instead my solution to this is like this:
The js ajax function:
function ajax(url,obj,callback){
var xhr=new XMLHttpRequest;
xhr.onreadystatechange=function(){
if(this.readyState==4){
if(callback){
callback(this.responseText);
}
}
}
xhr.open("POST",url);
xhr.send(JSON.stringify(obj));
}
Then on the server side, I read directly from the Request Body (sorry for the Go code, but I think you can also get the Request body from Python runtime?):
// read the request body
body, _ := ioutil.ReadAll(httpRequest.Body)
// parse the json payload
var user struct {
Email string
Password string
}
json.Unmarshal([]byte(body), &user)
Hope this helps
It seems like the Newly added FormData class in Javascript is getting rampant on line. Mostly when targeting multiple file uploads with Ajax. But some how it has some compatibility issues with most of IE if not 10+...
Should I have an HTML like:
<form id="normalForm" enctype="multipart/form-data" onSubmmit="gaga()">
<input name="aFile" id="aFile" type="file" multiple/>
<input type="button" value="go">
</form>
and the normal javaScript:
function gaga {
var f= document.getElementById("normalForm");
/// do something
}
or the a function with the New FormData:
function nGaga (){
var f= new FormData()
f.append("aFile", fileInputElement.files[0])
/// and whatever else to be appended
}
After going through some reading, i somehow learnt that, this is mostly used to generate "Key:value" object in Javascript. However, using jQuery, I could do somethinglike:
var f= $('#normalForm').serializeArray();
This will kinda give me the "Key:value" object.
So, why should one use the new FormData while dealing with XMLHTTPrequests despite it's issues? and what is the difference?
Say for instance that you wanted to upload a PDF but also needed to include some user generated metadata along with it as JSON or XML (or anything really). With FormData not only can we upload Files and strings (which is already possible with HTML forms) but we can also upload Blobs which allow us to upload dynamically generated content and be able to specify the content-type:
document.getElementById('dropzone').ondrop = function(e){
e.preventDefault();
uploadFiles(e.dataTransfer.files);
};
function uploadFiles(files) {
var nextId = 123;
var xml = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>";
var json = {
title: "Hello World!",
tags: ["pdf", "non-fiction", "literature"],
token: "ABCeasyAs123"
};
files.forEach(function(file, i) {
var formData = new FormData();
var xhr = new XMLHttpRequest();
json.id = nextId + i;
formData.append("XML", new Blob([ xml ], { type: "text/xml" });
formData.append("JSON", new Blob([ JSON.stringify(json) ], { type: "application/json" }));
formData.append("PDF", file);
xhr.open("POST", "/upload");
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var progress = (event.loaded / event.total * 100 | 0);
console.log('Upload #%d is %d% complete.', i+1, progress);
}
}
xhr.send(formData);
});
}
I have written file upload code that work fine for uploading a file and saving it in a folder.
I have included a functionality that allows the user to load URL of the PDF file and than the file from URL should be uploaded and saved.
The code:
function loadURL(box) {
var box = dhtmlx.modalbox({
title: "Load URL",
text: "<div id='form_in_box'><div>Enter the URL of PDF file <hr/><div><div><textarea id='file' style='width: 400px; height: 27px;'></textarea></div><span class='dhtmlx_button'><input type='submit' value='Load URL' style='width: 86px' onclick='save_file(this)'></span><span class='dhtmlx_button'><input type='button' value='Cancel' onclick='close_file(this)' style='width:80px;'></span></label></div></div>",
width: "300px"
})
}
function save_file(box) {
var file = document.getElementById('file');
if (file.value == "") {
alert("Choose a file to upload");
return false;
}
dhtmlx.modalbox.hide(box);
var fd = new FormData();
fd.append('file', file.files[0]);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/FileUpload/Upload', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert('File successfully uploaded to the server');
}
};
xhr.send(fd);
}
If i use the above code for load_URL am getting error as:
TypeError: file.files is undefined
fd.append('file', file.files[0]);
Don't use the Files API (which you can use for reading a local file from a file input). Just send the URL to the server and have your server side code fetch it.
Use WebClient class to download the file from the remote url. You can use the DownloadFile method to download a file from a remote url.
public ActionResult DownloadFile(string fileName)
{
if (!String.IsNullOrEmpty(fileName))
{
using (WebClient wc = new WebClient())
{
string targetPath = #"C:\YourFolder\thatUniqueFileName.pdf";
wc.DownloadFile(fileName,targetPath);
return RedirectToAction("Downloaded"); //PRG pattern
}
}
return VieW();
}
If you want to save the files in your App_Data folder of the project, you can change the targetPath variables value like this
string targetPath = HttpContext.Server.MapPath("~/App_Data/yourPdf.pdf");
You could parse the fileUrl and get the file name from that and add a unique identifier to that(to avoid overwriting of different files with same name) and use that to save the files.