For some reason, I can't seem to be able to perform both a multi-file upload with additional data on the same form.
Specs:
Python 3.10.4 (inside virtual environment)
blacksheep 1.2.5
Ubuntu 20.04
Here is my html code (simplified):
<form action='/upload_files_with_additional_data' method="post" enctype='multipart/form-data'>
<input type="file" name="files" multiple>
<label for="checkbox1" class="checkbox">Checkbox 1</label>
<input type="checkbox" name="checkbox1">
<label for="checkbox2" class="checkbox">Checkbox 2</label>
<input type="checkbox" name="checkbox2">
<label for="textfield">Text field</label>
<input type="text" id="textfield" name="textfield">
<input type="submit" value="Submit">
</form>
Here's what I've tried (but didn't work) and what I expect would work:
#post('/upload_files_with_additional_data')
async def upload_files_with_additional_data(self, files: FromFiles, form_data: FromForm):
for file in Files:
# do something with each file
# do something with additional form data
I've also tried using FromForm[FormData] with the following FormData:
class FormData:
checkbox1: bool
checkbo2: bool
files: list # also tried with type hint FromFIles (and without setting files alltogether
# def __init__(self, checkbox1: str, checkbox2: str, files): # also tried with files: FromFiles and without entire __init__ (but that didn't work at all)
# self.checkbox1 = bool(checkbox1)
# self.checkbox2 = bool(checkbox2)
# self.files = files # also tried with FromFiles(files)
Either I can upload files, without sending additional form data (using only files: FromFiles), or I can use the form data without the files (using only form: FromForm). Combining the two doesn't work.
When using async def upload_files_with_additional_data(self, request: Request): I can get also get the form data from there using form = await request.form(), but that only works for additional data, not the files. When I try to upload files I get:
form = await request.form()
File "blacksheep/messages.pyx", line 172, in form
File "blacksheep/contents.pyx", line 163, in blacksheep.contents.multiparts_to_dictionary
AttributeError: 'bytes' object has no attribute 'append'
I've also tried using async def upload_files_with_additional_data(self, bytes: FromBytes): which does seem to contain all the data, but as bytes, and I've tried a few ways to convert these to what I want, but no success there either.
Is there a way of uploading both multiple files, alongside other form data (I'm fairly confident there is, but I just haven't figured it out)? If so: how should this be achieved?
Related
For some reason, this won't accept json files.
#app.route('/get_data', methods=['POST'])
def get_data():
dataFile = request.files['file_path']
dataFileName = dataFile.filename
dataFile.save(os.path.join(uploads_dir, dataFileName))
I keep getting this error:
You seem to have json set as an file ending in your template by <input type="file" accept="json">. (The template is not supplied so I can't pinpoint the line. This is not an error of the backend (flask) but of the your template code (jinja/html). It would be nice if you could supply a MRE for such issues.
For more information about <input type="file"> take a look at the MDN Documentation.
Example of correct accept:
<input type="file" accept=".json">
This will only allow *.json file but keep in mind that users may supply other files manually and create a fallback or validation when parsing/ saving the file.
A .tar.bz2 file served from web.py is saved as .tar.bz2 from the browser. However when served by flask, the .bz2 extension is removed (but running file against the file still identifies it as a .bz2 file).
I have both frameworks running (web.py on python 2.7 and flask on python 3.7).
I manually put the same .tar.bz2 file in a directory and serve it from the web page.
Web.py:
=======
<form method="get" action="$fName">
<div id="formsubmitbutton">
<input type="submit" name="Retrieve File" value="Retrieve File" class="button3" />
</div>
</form>
Flask:
======
<form method="get" action="{{ fName }}">
<div id="formsubmitbutton">
<input type="submit" name="Retrieve File" value="Retrieve File" class="button3" />
</div>
</form>
I would expect both setups to return fName.tar.bz2. Chrome is returning the following and I have no clue why, but suspect it has something to do with my problem:
"Resource interpreted as Document but transferred with MIME type application/x-tar"
UPDATE
Thanks for the tip. From Chrome though, that was the entire error message, minus the actual file name since there is customer information in there. Here is is edited error:
Resource interpreted as Document but transferred with MIME type application/x-tar: "http://foo.example.com:5000/static/1564445156.7369618/filename.tar.bz2?Retrieve+SSD=Retrieve+SSD".
Also, there is no traceback from web.py or Flask, because there is no error. it's just that when you click on retrieve file, web.py returns the full filename with .bz2 extenstion, and Flask returns the file without the .bz2 extension.
The .bz2 files I am trying to serve are in the /static directory. By adding an app.route('/static') function, I was able to serve the file as a .bz2 without it being stripped of the .bz2 suffix:
#main.route('/static')
def sendfile():
for key,val in request.values.items():
filedir = '/home/admin/Flask/httsTools'+os.path.dirname(key)
filename = os.path.basename(key)
return send_from_directory(filedir, filename, as_attachment=True)
Whether or not this is the recommended way to do it I'm not sure, but it works.
Thank you for your help.
I have a small .py program, rendering 2 HTML pages. One of those HTML pages has a form in it. A basic form requesting a name, and a comment. I can not figure out how to take the name and the comment from the form and store it into the csv file. I have got the coding so that the very little I already manually input into the csv file is printed/returned on the HTML page, which is one of the goals. But I can't get the data I input into the form into the csv file, then back n the HTML page. I feel like this is a simple fix, but the Flask book makes absolutely no sense to me, I'm dyslexic and I find it impossible to make sense of the examples and the written explanations.
This is the code I have for reading the csv back onto the page;
#app.route('/guestbook')
def guestbook():
with open('nameList.csv','r') as inFile:
reader=csv.reader(inFile)
names=[row for row in reader]
return render_template('guestbook.html',names=names[1:])
And this is my form coding;
<h3 class="tab">Feel free to enter your comments below</h3>
<br />
<br />
<form action="" method="get" enctype="text/plain" name="Comments Form">
<input id="namebox" type="text" maxlength="45" size="32" placeholder="Name"
class="tab"/>
<br />
<textarea id="txt1" class="textbox tab" rows="6" placeholder="Your comment"
class="tab" cols="28"></textarea>
<br />
<button class="menuitem tab" onclick="clearComment()" class="tab">Clear
comment</button>
<button class="menuitem" onclick="saveComment()" class="tab">Save comment</button>
<br>
</div>
By what I understand all you need is to save the data into the file and you don't know how to handle this in Flask, I'll try to explain it with code as clear as possible:
# request is a part of Flask's HTTP requests
from flask import request
import csv
# methods is an array that's used in Flask which requests' methods are
# allowed to be performed in this route.
#app.route('/save-comment', methods=['POST'])
def save_comment():
# This is to make sure the HTTP method is POST and not any other
if request.method == 'POST':
# request.form is a dictionary that contains the form sent through
# the HTTP request. This work by getting the name="xxx" attribute of
# the html form field. So, if you want to get the name, your input
# should be something like this: <input type="text" name="name" />.
name = request.form['name']
comment = request.form['comment']
# This array is the fields your csv file has and in the following code
# you'll see how it will be used. Change it to your actual csv's fields.
fieldnames = ['name', 'comment']
# We repeat the same step as the reading, but with "w" to indicate
# the file is going to be written.
with open('nameList.csv','w') as inFile:
# DictWriter will help you write the file easily by treating the
# csv as a python's class and will allow you to work with
# dictionaries instead of having to add the csv manually.
writer = csv.DictWriter(inFile, fieldnames=fieldnames)
# writerow() will write a row in your csv file
writer.writerow({'name': name, 'comment': comment})
# And you return a text or a template, but if you don't return anything
# this code will never work.
return 'Thanks for your input!'
This question already has answers here:
Displaying uploaded file in GAE Appengine with Python
(3 answers)
Closed 8 years ago.
I'm uploading a small pdf file to be stored as a blob in the Datastore.
Here's the uploading html, getting the PDF from the user:
<form action="/" method="post" enctype="multipart/form-data">
<input type="file" name="pdf">
<input type="submit" value="Upload">
</form>
Here's the handler, storing the PDF to the Datastore:
def post(self):
p = self.request.POST['pdf']
if p:
person.pdf = p.value
Here's the view, showing the user the contents of the PDF:
<embed src="{{ person.pdf }}" width="500"
height="375" type="application/pdf">
According to all the information I have found, the content of the PDF should reside in p.value. However, the person.pdf attribute is None, and, of course, nothing is displayed.
The most basic way that this appears to be wrong is that:
<embed src="{{ person.pdf }}">
should contain the URL to download the pdf file. However, you're uploading a file via your upload form, and presumably storing the file data.
There's at least a few things that can go wrong, you should debug through and at the very least, isolate where something is going wrong:
Are you actually uploading the file?
How is the file encoded as it's uploaded?
How is the file decoded when you get it from the POST data?
How are you storing the actual file in person.pdf?
And finally, are you actually saving person after you modify it? It's generally more helpful to show all your code rather than snippets. And if that's all your code, where on earth is person coming from? It's not actually being initialized anywhere.
I would like that users of my ZOPE/Plone website can upload (big) file (>1Gb) on a server.
I have a form in html :
<form enctype="multipart/form-data" action="upload.py" method="post">
<p>File: <input type="file" name="file"></p>
<p><input type="submit" value="Upload"></p>
</form>
I have an external script with ZOPE : upload.py
def get(self, REQUEST):
filename = REQUEST.file['file']
Unfortunately I don't know what to do with this file..
I found some tutorial but I think I'm on wrong way (because these methods can't work with ZOPE ?):
CGI : http://webpython.codepoint.net/cgi_file_upload
ftplib : Python Script Uploading files via FTP
Thanks for your advices,
It depends on how and where you want to store it.
The REQUEST.file is a file object where you can read, seek, tell etc the contents from.
You can store it like a blob:
from ZODB.blob import Blob
blob = Blob()
bfile = blob.open('w')
bfile.write(REQUEST.file)
bfile.close()
# save the blob somewhere now
context.myfile = blob