First, I don't need any functions more than upload a file. No progress bar, no file type, or size check, no multiple files.
What I want is the most simple HTML webpage to handle the upload and save the file with the name I specified.
I tried to use this:
<form action="../cgi-bin/upload.py" method="post" enctype="multipart/form-data">
<input type="file" name="upload" />
<input type="submit" /></form>
In upload.py:
#!/usr/bin/python
import os
import commands
import cgi, cgitb
cgitb.enable()
print "Content-Type: text/html"
print
print 'start!'
form = cgi.FieldStorage()
filedata = form['upload']
But I don't know how to save this in file, like "Beautiful.mp3".
Can any body help?
Though, really, I don't want to use any scripts. I just want the most basic html pages. Python scripts will only exist when there must be some CGI handlers. Flash is not preferred.
The filedata object will wrap a file-like object that can be treated like a regular file. Basically you would do this:
if filedata.file: # field really is an upload
with file("Beautiful.mp3", 'w') as outfile:
outfile.write(filedata.file.read())
Or, you could do just about anything else with it, using read(), readlines() or readline()
Related
I'm using python pandas and flask for some postprocessing tasks (anlaysis and visualization). Until now I uploaded/read *.csv *.xlsx and *.xls via pd.read_csv, pd.read_xlsx. Everything worked quiet fine.
Now I have a *.xml file as datasource and tried according my habit pattern.
So i tried:
<form action="/input" method="POST" enctype="multipart/form-data">
<input class="form-control" type="file" name="file">
<input type="submit" class="btn btn-outline-secondary" name="Preview" value ="Preview Data" > </input>
from flask import Flask, render_template,request, render_template
import pandas as pd
import xml.etree.ElementTree as ET
#app.route("/input", methods=['POST', 'GET'])
def input():
if request.method == 'POST':
if request.form['Preview'] == "Preview Data":
file = request.files['file']
filename = file.filename
if '.xml' in filename:
content = pd.read_xml(file, parser='lxml')
But when I pass a .xml file to the app via the form. I get the error:
File "C:\ProgramData\MiniforgeEnvs\TestEnv\lib\site-packages\pandas\io\xml.py", line 627, in _parse_doc
with preprocess_data(handle_data) as xml_data:
AttributeError: __enter__
I tried check different options:
when I use the inbuild xml.etree package it works fine:
import xml.etree.ElementTree as ET
if '.xml' in filename:
tree = ET.parse(file)
root = tree.getroot()
print(root[1][0][1].attrib)
when I load the .xml direct from the app directory into pd.read_xml() it also works fine:
if '.xml' in filename:
content = pd.read_xml('SampleExport.xml', parser='lxml')
I tried different prasers: "lxml" and "etree"
But at the end when I pass the .xml via the Form/input and using pd.read_xml(file,parser='lxml') I got the error from above.
I just solved my issue even though I'm not quite sure why pd.read_xml() behaves different compared to pd.read_csv() or pd.read_xlsx().
pd.read_xml is not able to read a FileStorage object. The variable passed by request.file[] is a instance of the class: werkzeug.datastructures.FileStorage(stream=None, filename=None, name=None, content_type=None, content_length=None, headers=None).
Via the read function I extracted the file itsself.
filestorage = request.files['file']
file=filestorage.read()
with this passed to pd.read_xml it works fine.
Is there anybody who can explain why _parse_doc() funtion of pd.read_xml() is not able to read FileStotage type?
I am creating a tool where either
A new XLSX file is generated for the user to download
The user can upload an XLSX file they have, I will read the contents of that file, aand use them to generate a new file for the user to download.
I would like to make use of Pandas to read the XLSX file into a dataframe, so I can work with it easily. However, I can't get it working. Can you help me?
Example extract from CGI file:
import pandas as pd
import cgi
from mako.template import Template
from mako.lookup import TemplateLookup
import http.cookies as Cookie
import os
import tempfile
import shutil
import sys
cookie = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE"))
method = os.environ.get("REQUEST_METHOD", "GET")
templates = TemplateLookup(directories = ['templates'], output_encoding='utf-8')
if method == "GET": # This is for getting the page
template = templates.get_template("my.html")
sys.stdout.flush()
sys.stdout.buffer.write(b"Content-Type: text/html\n\n")
sys.stdout.buffer.write(
template.render())
if method == "POST":
form = cgi.FieldStorage()
print("Content-Type: application/vnd.ms-excel")
print("Content-Disposition: attachment; filename=NewFile.xlsx\n")
output_path = "/tmp/" + next(tempfile._get_candidate_names()) + '.xlsx'
data = *some pandas dataframe previously created*
if "editfile" in form:
myfilename = form['myfile'].filename
with open(myfilename, 'wb') as f:
f.write(form['myfile'].file.read())
data = pd.read_excel(myfilename)
data.to_excel(output_path)
with open(path, "rb") as f:
sys.stdout.flush()
shutil.copyfileobj(f, sys.stdout.buffer)
Example extract from HTML file:
<p>Press the button below to generate a new version of the xlsx file</p>
<form method=post>
<p><input type=submit value='Generate new version of file' name='newfile'>
<div class="wrapper">
</div>
</form>
<br>
<p>Or upload a file.</p>
<p>In this case, a new file will be created using the contents of this file.</p>
<form method="post" enctype="multipart/form-data">
<input id="fileupload" name="myfile" type="file" />
<input value="Upload and create new file" name='editfile' type="submit" />
</form>
This works without the if "editfile" in form: bit so I know something is going wrong when I am trying to access the file that the user has uploaded.
The problem is that whilst a file is created, the created file has a file size of 0 KB and will not open in Excel. Crucially, the file that the user has uploaded can not be found in the location that I have written it out.
You've passed myfilename to pandas; however that file doesn't exist on the server yet. You'll have to save the file somewhere locally first before using it.
The following will download the file to the current directory (same directory as the CGI script). Of course, you're welcome to save it to some more suitable directory, depending on your setup.
form = cgi.FieldStorage()
myfilename = form['myfile'].filename
with open(myfilename, 'wb') as f: # Save the file locally
f.write(form['myfile'].file.read())
data = pd.read_excel(myfilename)
I have a button export :
<button class="aptButton" formaction="/export/" type="submit">export</button>
and I have this in the /export/
index.cgi
#! /apollo/sbin/envroot $ENVROOT/bin/python
# -*- coding: utf-8 -*-
import cgitb
cgitb.enable()
import cgi
def main():
print "Content-Type: text/html"
print
form = cgi.FieldStorage()
results = helpers.getResults()
environment = helpers.get_environment()
print environment.get_template('export.html').render(
results = results)
main()
and I have this in my export.html
<!doctype html>
{% for id in results %}
{{ write_results_to_file(id) }}
{% endfor %}
I am trying to download the results to a tab separated file, so I thought of writing to a local file and then send(download) the file but I am not sure how to do the download part, I couldnt use flask or django which has some good libs.. is there any other lib which I can use to download the results to a tab delimited file on the users desktop?
export.py
def write_results_to_file(result):
local_filename = "/home/testing.txt"
# NOTE the stream=True parameter
with open(local_filename, 'w') as f:
f.write('\t'.join(result) + '\n')
If you're using good old-fashioned CGI to produce a tab-separated file,
all you need to do is print an appropriate header and then print the content on stdout, something like this:
def main():
form = cgi.FieldStorage()
results = helpers.getResults()
print "Content-Type: text/plain"
print "Content-Disposition: attachment; filename=testing.txt"
print
for result in results:
print '\t'.join(result) + '\n'
main()
The essential parts are the 2 lines that print the header,
followed by a blank line to separate from the content,
followed by the plain text content.
If you want to make this happen on the click of an Export button,
then you can, for example:
Make the Export button a link to another URL endpoint that will use the example script I put above
Or, use the same script, with a conditional statement on form parameters to decide to print the front page, or to print the content using the example script above
Let me know if you need further help.
I am making a data visualization tool that takes input from the user (choosing a file on the computer); processes it in Python with Pandas, Numpy, etc; and displays the data in the browser on a local server.
I am having trouble accessing the data once the file is selected using an HTML input form.
HTML form:
<form action="getfile" method="POST" enctype="multipart/form-data">
Project file path: <input type="file" name="myfile"><br>
<input type="submit" value="Submit">
</form>
Flask routing:
#app.route("/")
def index():
return render_template('index.html')
#app.route('/getfile', methods=['GET','POST'])
def getfile():
if request.method == 'POST':
result = request.form['myfile']
else:
result = request.args.get['myfile']
return result
This returns a "Bad Request The browser (or proxy) sent a request that this server could not understand." error. I have tried a number of different ways of getting the data out of the file and simply printing it to the screen to start, and have received a range of errors including "TypeError: 'FileStorage' object is not callable" and "ImmutableMultiDict' object is not callable". Any pointers on how to approach this task correctly are appreciated.
Try this. I've been working on saving and unzipping files for the last few days. If you have any trouble with this code, let me know :)
I'd suggest saving the file on disk and then reading it. If you don't want to do that, you needn't.
from flask import Flask, render_template, request
from werkzeug import secure_filename
#app.route('/getfile', methods=['GET','POST'])
def getfile():
if request.method == 'POST':
# for secure filenames. Read the documentation.
file = request.files['myfile']
filename = secure_filename(file.filename)
# os.path.join is used so that paths work in every operating system
file.save(os.path.join("wherever","you","want",filename))
# You should use os.path.join here too.
with open("wherever/you/want/filename") as f:
file_content = f.read()
return file_content
else:
result = request.args.get['myfile']
return result
And as zvone suggested in the comments, I too would advise against using GET to upload files.
Uploading files
os.path by Effbot
Edit:-
You don't want to save the file.
Uploaded files are stored in memory or at a temporary location on the filesystem. You can access those files by looking at the files attribute on the request object. Each uploaded file is stored in that dictionary. It behaves just like a standard Python file object, but it also has a save() method that allows you to store that file on the filesystem of the server.
I got this from the Flask documentation. Since it's a Python file you can directly use file.read() on it without file.save().
Also if you need to save it for sometime and then delete it later, you can use os.path.remove to delete the file after saving it. Deleting a file in Python
A input type=file data isn't passed in as the form dictionary of a request object. It is passed in as request.files (files dictionary in the request object).
So simply change:
result = request.form['myfile']
to
result = request.files['myfile']
I am on a windows 10 laptop.
When i manually open submit.html on my local computer click and browse to namo.jpg namo.png and then submit, i get the website processing my image and return with result file within 15 seconds.
But I can't seem to get it to do the same using Python mechanize, when it run the script, the mechanize_results.html file keeps returning too quickly and telling me in their page that "Uploaded file is not a valid image. Only JPG, PNG and GIF files are allowed.. "
Not sure what i have to change to get the site to recognize my file submitted by my python mechanize script as an image file.
my submit.html file has this
<form name="myform" id="myform" action="http://deepdreamgenerator.com/upload-im" enctype="multipart/form-data" method="POST" id="upload-form">
<input type="hidden" name="_token" value="pfC1a6HGVdbWO7mCmKVkqVinCkSYOKkQxXZV9NY1">
<input type="file" name="file" id="upload"/>
<input type="submit" />
</form>
my python mechanize script has this
import mechanize
filename = 'C:/Users/tintran/Desktop/namo.png'
url = "file:///C:/Users/tintran/Desktop/submit.html"
br = mechanize.Browser()
br.set_handle_robots(False) # ignore robots
br.open(url)
br.select_form('myform')
br.set_all_readonly(False)
br.form.add_file(open(filename,'r'))
res = br.submit()
content = res.read()
with open("mechanize_results.html", "w") as f:
f.write(content)
https://docs.python.org/2/library/functions.html#open
If mode is omitted, it defaults to 'r'. The default is to use text mode, which may convert '\n' characters to a platform-specific representation on writing and back on reading. Thus, when opening a binary file, you should append 'b' to the mode value to open the file in binary mode, which will improve portability. (Appending 'b' is useful even on systems that don’t treat binary and text files differently, where it serves as documentation.) See below for more possible values of mode.
It's all about Windows here. So just use 'rb' for opening PNG file.