Direct Upload to S3 Using Python/Boto/Django to Construct Policy - python

I have been through many iterations of this problem so far, searched out many different examples, and have been all through the documentation.
I am trying to combine Plupload (http://www.plupload.com/) with the AWS S3 direct post method (http://aws.amazon.com/articles/1434). However, I believe there's something wrong with the way I am constructing my policy and signature for transmission. When I submit the form, I don't get a response from the server, but rather my connection to the server is reset.
I have attempted using the python code in the example:
import base64
import hmac, sha
policy = base64.b64encode(policy_document)
signature = base64.b64encode(
hmac.new(aws_secret_key, policy, sha).digest())
I have also tried to use the more up-to-date hashlib library in python. Whatever method I use to construct my policy and signature, I always get different values than those generated here:
http://s3.amazonaws.com/doc/s3-example-code/post/post_sample.html
I have read through this question:
How do I make Plupload upload directly to Amazon S3?
But I found the examples provided to be overly complicated and wasn't able to accurately implement them.
My most recent attempts have been to use portions of the boto library:
http://boto.cloudhackers.com/ref/s3.html#module-boto.s3.connection
But using the S3Commection.build_post_form_args method has not worked for me either.
If anyone could provide a proper example of how to create the post form using python, I would very much appreciate it. Even some simple insights on why the connection is always reset would be nice.
Some caveats:
I would like to use hashlib if possible.
I want to get an XML response from Amazon (presumably "success_action_status = '201'" does this)
I need to be able to upload largish type files, max size ~2GB.
One final note, when I run this in Chrome, it provides upload progress, and the upload usually fails around 37%.

Nathan's answer helped get me started. I've included two solutions that are currently working for me.
The first solution uses plain Python. The second uses boto.
I tried to get boto working first, but kept getting errors. So I went back to the Amazon ruby documentation and got S3 to accept files using python without boto. (Browser Uploads to S3 using HTML POST)
After understanding what was going on, I was able to fix my errors and use boto, which is a simpler solution.
I'm including solution 1 because it shows explicitly how to setup the policy document and signature using python.
My goal was to create the html upload page as a dynamic page, along with the "success" page the user sees after a successful upload. Solution 1 shows the dynamic creation of the form upload page, while solution 2 shows the creation of both the upload form page and the success page.
Solution 1:
import base64
import hmac, hashlib
###### EDIT ONLY THE FOLLOWING ITEMS ######
DEBUG = 1
AWS_SECRET_KEY = "MySecretKey"
AWS_ACCESS_KEY = "MyAccessKey"
HTML_NAME = "S3PostForm.html"
EXPIRE_DATE = "2015-01-01T00:00:00Z" # Jan 1, 2015 gmt
FILE_TO_UPLOAD = "${filename}"
BUCKET = "media.mysite.com"
KEY = ""
ACL = "public-read" # or "private"
SUCCESS = "http://media.mysite.com/success.html"
CONTENT_TYPE = ""
CONTENT_LENGTH = 1024**3 # One gigabyte
HTTP_OR_HTTPS = "http" # Or "https" for better security
PAGE_TITLE = "My Html Upload to S3 Form"
ACTION = "%s://%s.s3.amazonaws.com/" % (HTTP_OR_HTTPS, BUCKET)
###### DON'T EDIT FROM HERE ON DOWN ######
policy_document_data = {
"expire": EXPIRE_DATE,
"bucket_name": BUCKET,
"key_name": KEY,
"acl_name": ACL,
"success_redirect": SUCCESS,
"content_name": CONTENT_TYPE,
"content_length": CONTENT_LENGTH,
}
policy_document = """
{"expiration": "%(expire)s",
"conditions": [
{"bucket": "%(bucket_name)s"},
["starts-with", "$key", "%(key_name)s"],
{"acl": "%(acl_name)s"},
{"success_action_redirect": "%(success_redirect)s"},
["starts-with", "$Content-Type", "%(content_name)s"],
["content-length-range", 0, %(content_length)d]
]
}
""" % policy_document_data
policy = base64.b64encode(policy_document)
signature = base64.b64encode(hmac.new(AWS_SECRET_KEY, policy, hashlib.sha1).digest())
html_page_data = {
"page_title": PAGE_TITLE,
"action_name": ACTION,
"filename": FILE_TO_UPLOAD,
"access_name": AWS_ACCESS_KEY,
"acl_name": ACL,
"redirect_name": SUCCESS,
"policy_name": policy,
"sig_name": signature,
"content_name": CONTENT_TYPE,
}
html_page = """
<html>
<head>
<title>%(page_title)s</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="%(action_name)s" method="post" enctype="multipart/form-data">
<input type="hidden" name="key" value="%(filename)s">
<input type="hidden" name="AWSAccessKeyId" value="%(access_name)s">
<input type="hidden" name="acl" value="%(acl_name)s">
<input type="hidden" name="success_action_redirect" value="%(redirect_name)s">
<input type="hidden" name="policy" value="%(policy_name)s">
<input type="hidden" name="signature" value="%(sig_name)s">
<input type="hidden" name="Content-Type" value="%(content_name)s">
<!-- Include any additional input fields here -->
Browse to locate the file to upload:<br \> <br \>
<input name="file" type="file"><br> <br \>
<input type="submit" value="Upload File to S3">
</form>
</body>
</html>
""" % html_page_data
with open(HTML_NAME, "wb") as f:
f.write(html_page)
###### Dump output if testing ######
if DEBUG:
if 1: # Set true if not using the LEO editor
class G:
def es(self, data):print(data)
g = G()
items = [
"",
"",
"policy_document: %s" % policy_document,
"ploicy: %s" % policy,
"signature: %s" % signature,
"",
"",
]
for item in items:
g.es(item)
Solution 2:
from boto.s3 import connection
###### EDIT ONLY THE FOLLOWING ITEMS ######
DEBUG = 1
AWS_SECRET_KEY = "MySecretKey"
AWS_ACCESS_KEY = "MyAccessKey"
HTML_NAME = "S3PostForm.html"
SUCCESS_NAME = "success.html"
EXPIRES = 60*60*24*356 # seconds = 1 year
BUCKET = "media.mysite.com"
KEY = "${filename}" # will match file entered by user
ACL = "public-read" # or "private"
SUCCESS = "http://media.mysite.com/success.html"
CONTENT_TYPE = "" # seems to work this way
CONTENT_LENGTH = 1024**3 # One gigabyte
HTTP_OR_HTTPS = "http" # Or https for better security
PAGE_TITLE = "My Html Upload to S3 Form"
###### DON'T EDIT FROM HERE ON DOWN ######
conn = connection.S3Connection(AWS_ACCESS_KEY,AWS_SECRET_KEY)
args = conn.build_post_form_args(
BUCKET,
KEY,
expires_in=EXPIRES,
acl=ACL,
success_action_redirect=SUCCESS,
max_content_length=CONTENT_LENGTH,
http_method=HTTP_OR_HTTPS,
fields=None,
conditions=None,
storage_class='STANDARD',
server_side_encryption=None,
)
form_fields = ""
line = ' <input type="hidden" name="%s" value="%s" >\n'
for item in args['fields']:
new_line = line % (item["name"], item["value"])
form_fields += new_line
html_page_data = {
"page_title": PAGE_TITLE,
"action": args["action"],
"input_fields": form_fields,
}
html_page = """
<html>
<head>
<title>%(page_title)s</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="%(action)s" method="post" enctype="multipart/form-data" >
%(input_fields)s
<!-- Include any additional input fields here -->
Browse to locate the file to upload:<br \> <br \>
<input name="file" type="file"><br> <br \>
<input type="submit" value="Upload File to S3">
</form>
</body>
</html>
""" % html_page_data
with open(HTML_NAME, "wb") as f:
f.write(html_page)
success_page = """
<html>
<head>
<title>S3 POST Success Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="jquery.js"></script>
<script src="purl.js"></script>
<!--
Amazon S3 passes three data items in the url of this page if
the upload was successful:
bucket = bucket name
key = file name upload to the bucket
etag = hash of file
The following script parses these values and puts them in
the page to be displayed.
-->
<script type="text/javascript">
var pname,url,val,params=["bucket","key","etag"];
$(document).ready(function()
{
url = $.url();
for (param in params)
{
pname = params[param];
val = url.param(pname);
if(typeof val != 'undefined')
document.getElementById(pname).value = val;
}
});
</script>
</head>
<body>
<div style="margin:0 auto;text-align:center;">
<p>Congratulations!</p>
<p>You have successfully uploaded the file.</p>
<form action="#" method="get"
>Location:
<br />
<input type="text" name="bucket" id="bucket" />
<br />File Name:
<br />
<input type="text" name="key" id="key" />
<br />Hash:
<br />
<input type="text" name="etag" id="etag" />
</form>
</div>
</body>
</html>
"""
with open(SUCCESS_NAME, "wb") as f:
f.write(success_page)
###### Dump output if testing ######
if DEBUG:
if 1: # Set true if not using the LEO editor
class G:
def es(self, data):print(data)
g = G()
g.es("conn = %s" % conn)
for key in args.keys():
if key is not "fields":
g.es("%s: %s" % (key, args[key]))
continue
for item in args['fields']:
g.es(item)

I tried using Boto but found it didn't let me put in all of the headers I wanted. Below you can see what I do to generate the policy, signature, and a dictionary of post form values.
Note that all of the x-amz-meta-* tags are custom header properties and you don't need them. Also notice that pretty much everything that is going to be in the form needs to be in the policy that gets encoded and signed.
def generate_post_form(bucket_name, key, post_key, file_id, file_name, content_type):
import hmac
from hashlib import sha1
from django.conf import settings
policy = """{"expiration": "%(expires)s","conditions": [{"bucket":"%(bucket)s"},["eq","$key","%(key)s"],{"acl":"private"},{"x-amz-meta-content_type":"%(content_type)s"},{"x-amz-meta-file_name":"%(file_name)s"},{"x-amz-meta-post_key":"%(post_key)s"},{"x-amz-meta-file_id":"%(file_id)s"},{"success_action_status":"200"}]}"""
policy = policy%{
"expires":(datetime.utcnow()+settings.TIMEOUT).strftime("%Y-%m-%dT%H:%M:%SZ"), # This has to be formatted this way
"bucket": bucket_name, # the name of your bucket
"key": key, # this is the S3 key where the posted file will be stored
"post_key": post_key, # custom properties begin here
"file_id":file_id,
"file_name": file_name,
"content_type": content_type,
}
encoded = policy.encode('utf-8').encode('base64').replace("\n","") # Here we base64 encode a UTF-8 version of our policy. Make sure there are no new lines, Amazon doesn't like them.
return ("%s://%s.s3.amazonaws.com/"%(settings.HTTP_CONNECTION_TYPE, self.bucket_name),
{"policy":encoded,
"signature":hmac.new(settings.AWS_SECRET_KEY,encoded,sha1).digest().encode("base64").replace("\n",""), # Generate the policy signature using our Amazon Secret Key
"key": key,
"AWSAccessKeyId": settings.AWS_ACCESS_KEY, # Obviously the Amazon Access Key
"acl":"private",
"x-amz-meta-post_key":post_key,
"x-amz-meta-file_id":file_id,
"x-amz-meta-file_name": file_name,
"x-amz-meta-content_type": content_type,
"success_action_status":"200",
})
The returned tuple can then be used to generate a form that posts to the generated S3 url with all of the key value pairs from the dictionary as hidden fields and your actual file input field, whose name/id should be "file".
Hope that helps as an example.

I've been struggling with this same exact problem, using almost the same exact code, for days. ( See Python Generated Signature for S3 Post ) Just tried to encode my policy in accordance with White Box Dev's code, but still not coming up with the same as AWS suggests I should have. I eventually gave up and used...
http://s3.amazonaws.com/doc/s3-example-code/post/post_sample.html
...and just inserted the values it returns into the HTML form. Works great.
#Mr. Oodles: if you're storing your aws_secret_key in a separate file, use bash command ls -al to check it's number of bytes prior to generating the signature. It should be 40 bytes long. As White Box Dev points out, AWS does not like \n, and it's possible you're bundling up this hidden character (or a carriage return or ^M) with your aws_secret_key string when saving it...hence making it 41 bytes long. You can try .replace("\n", "") or .rstrip() to get rid of it after reading it into your script, The .encode("utf-8") may work for you, too. None of those worked for me, however. Curious if you're running Python on Windows or Unix... You could also try using emacs to save the string without the \n being automatically inserted by your editor.

Try checking out https://github.com/burgalon/plupload-s3mixin
It combines PLUPLOAD with direct S3 upload

Related

How to use AJAX in Flask to iterate through a list

Problem
I am trying to display image-files using AJAX in Flask. More specifically, I want to display an image once a button is clicked and display the next image once the button is clicked again (like a slide-show). The filenames of the images are stored in my database. I query the database to get a list of filenames for the current user and combine each filename with the rest of the path (path to where the images are stored on disk) in order to display the image.
So far, I manage to get the first image of the current user. However, I can't figure out a way to keep track of which image is the next one to show.
I tried using a global variable as a counter (file_counter) which should serve as an index. I want to increase file_counter by 1 each time an ajax request is made in order to get the next file but the counter does not increase upon subsequent calls nor does it throw an error.
Question
How do I need to initialize the global variable (file_counter) in order for it to store its value across multiple calls? Furthermore, is the usage of global variables the correct way of doing this?
HTML
<div id="ajax-field"></div>
<button class="btn btn-block" id="next-button"><p>Next Image!</p></button>
AJAX:
$('#next-button').click(function(){
$("#ajax-field").text("");
$.ajax({
url: "/get_data",
type: "POST",
success: function(resp){
$('#ajax-field').append(resp.data);
}
});
});
Routing:
global filenames
global file_count
#app.route("/get_data", methods=['POST'])
def get_data():
try: # Is intended to fail on the first run in order for the global variables to be initialized. However it keeps failing on subsequent runs
display_img = filenames[file_count]
file_count +=1
except:
filenames = []
# current_user.uploads returns all file-objects of the current user
user_uploads = current_user.uploads
for file in user_uploads:
# file.filename returns the respective filename of the image
filenames.append(file.filename)
#filenames is now a list of filenames i.e. ['a.jpg','b.jpg','c.jpg'...]
display_img = filenames[0]
file_count = 1
path = "image_uploads/4_files/"+display_img
return jsonify({'data': render_template('ajax_template.html', mylist = path)})
ajax_template.html:
<ul>
{% block content %}
<li>
<img id="selected-image-ajax" src="{{url_for('static',filename=mylist)}}" class="img-thumbnail" style="display:block; margin:auto;"></img>
</li>
{% endblock content %}
</ul>
As #roganjosh pointed out, a session is the optimal way to store information across multiple requests. This solution presents an implementation of the photo display using flask.session to store the counter:
import flask, random, string
app = flask.Flask(__name__)
app.secret_key = ''.join(random.choice(string.printable) for _ in range(20))
#to use flask.session, a secret key must be passed to the app instance
#app.route('/display_page', methods=['GET'])
def display_page():
'''function to return the HTML page to display the images'''
flask.session['count'] = 0
_files = [i.filename for i in current_user.uploads]
return flask.render_template('photo_display.html', photo = _files[0])
#app.route('/get_photo', methods=['GET'])
def get_photo():
_direction = flask.request.args.get('direction')
flask.session['count'] = flask.session['count'] + (1 if _direction == 'f' else - 1)
_files = [i.filename for i in current_user.uploads]
return flask.jsonify({'photo':_files[flask.session['count']], 'forward':str(flask.session['count']+1 < len(_files)), 'back':str(bool(flask.session['count']))})
The display_page function will be called when a user accesses the /display_page route and will set the count to 0. get_photo is bound to the /get_photo route and will be called when the ajax request is sent.
photo_display.html:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<div class='image_display'>
<img src="{{photo}}" id='photo_display' height="100" width="100">
<table>
<tr>
<td class='back'></td>
<td class='forward'><button id='go_forward' class='navigate'>Forward</button></td>
</tr>
</table>
</div>
</body>
<script>
$(document).ready(function(){
$('.image_display').on('click', '.navigate', function(){
var direction = 'b';
if ($(this).prop('id') === 'go_forward'){
direction = 'f';
}
$.ajax({
url: "/get_photo",
type: "get",
data: {direction: direction},
success: function(response) {
$('#photo_display').attr('src', response.photo);
if (response.back === "True"){
$('.back').html("<button id='go_back' class='navigate'>Back</button>")
}
else{
$('#go_back').remove();
}
if (response.forward === "True"){
$('.forward').html("<button id='go_forward' class='navigate'>Forward</button>")
}
else{
$('#go_forward').remove();
}
},
});
});
});
</script>
</html>
The javascript in display_page.html communicates with the backend, and updates the img tag src accordingly. The script adds or removes the navigation buttons, depending on the current count value.
Demo:
To test the solution above, I created an image folder to store random photographs to display:

Dynamically update image using Python Flask AJAX

I have 1 very simple web application I am building right now but am very new to flask and jinja (and web development as a whole actually).
I have a watch folder, which will be getting an image sent to it via ftp on a pulse for ever. This wtch folder will only ever have one image in. Every 1 minute, the old image is replaced by a new image, with a new timestamp.
I would like to dynamically update the page, (and displayed timestamp) on a pulse as well, without having to reload any banners or static images that I will add later. I only want to update the following two lines out of the "Channels.Jinja" sample to follow.
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
Channels.Jinja
<!DOCTYPE HTML>
<html>
<head>
<title>Training</title>
</head>
<body bgcolor=white>
<div id=main>
<br>Date and Time of Screenshot <br/>
<br>{{screenshot_datetime}}<br/>
<img src={{screenshot_location}} width="100%"/>
</div>
<div id='test'>
<p>
<script>
var myVar=setInterval(function(){get_image()},1000);
function get_image() {
$.ajax({
type: 'GET',
cache: false,
url: 'get_data',
success: function({{data}}) {
$('img').attr('src', data);
}
});
}
</script>
</p>
</div>
</body>
</html>
Channels.py
def render_channel_route(cr):
static_folder = os.path.join('static',cr)
file_list = os.listdir(static_folder)
channel_files = [f for f in file_list if f.startswith(cr)]
if not channel_files :
logger.error('Could not find image file for Channel. File should start with {0}'.format(cr))
abort(404)
img = os.path.join(static_folder,file_list[0])
ts = get_time_from_filename(file_list[0],cr)
return render_template('Channels.jinja',screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
#app.route('/channel01-10')
def first_tab():
return render_channel_route('channel01-10')
#app.route('/get_data', methods=['GET'])
def get_data():
return render_template('Channels.jinja',
screenshot_datetime=time.strftime('%c',ts),screenshot_location=img)
Im at a loss, Ive been bumbling around for a while now. Any and all advice is welcome! I am seeing a 304 response upon refresh, but not even the timer i am trying to put on it is working. Pardon sloppy code, highly volatile code is getting changed often -_-
I don't know it there is a "special" way to deal with Ajax using some Flask extension, but in the "normal" Ajax flow first you need to use url_for to put the correct url in your Ajax call and return the data formatted in some way (in my example in JSON) and not to render the template again:
$.ajax({
type: 'GET',
cache: false,
url: "{{ url_for('get_data') }}",
success: function(resp){
$('img').attr('src', resp.url);
$('#sst').html(resp.time);
}
});
So, in your get_data function in your controller you have to get the time and the path again for your image an then return some like this (to fit in my example before):
from flask import json
#app.route('/get_data', methods=['GET'])
def get_data():
#get time and path
time=...
path=...
return json.dumps({time:time,url:path}), 200, {'Content-Type':'application/json'}
Look that I use $('#sst') so you have to put in your HTML:
<br><span id='sst'>{{screenshot_datetime}}</span><br/>

Looking for wsgi way of processing forms like mod_python Publisher

In mod_python Publisher, I could write a file loader, (uploader.py in this example) using code like this:
def index():
html = '''
<html>
<body>
<form id="fileUpload" action="uploader.py/upload" enctype="multipart/form-data" method="post">
<input type="file" id="file" name="file_name"/>
<input type="submit" value="Upload"/>
</form>
</body>
</html>
'''
return html
def upload(req, file_name):
fd = open("/mydir/myfile", "w")
for line in file_name.file.readlines():
fd.write(line)
fd.close()
This example, excludes any error checking but illustrates what I could do with mod_python and Publisher. The key things are that Publisher was able to call my function, upload, in the file uploader.py and pass it an argument, file_name with the attribute file with the file that was selected in the form.
What I want to do is to do the same thing in mod_wsgi. I'm looking for a way to call a function like this from a form and invoke my function in a similar fashion. I've looked at webob but cannot figure out how to do the same thing with this package. I would appreciate any help in coding a similar solution without having to use mod_python and Publisher.
Answer migrated from question text.
After some digging, I figured out a solution to this problem. This is the solution I came up with. I implemented this with two files: uploader1.py and uploader2.py. This is code for uploader1.py:
def application(environ, start_response):
from webob import Response
html = '''
<html>
<body>
<h1>Upload</h1>
<form id="fileUpload" action="uploader2.py" enctype="multipart/form-data" method="post">
<input type="file" name="filename"/>
<input type="submit" value="Upload"/>
</form>
</body>
</html>
'''
response = Response(body = html,
content_type = "text/html",
charset = "utf8",
status = "200 OK")
return response(environ, start_response)
And this is the code for uploader2.py:
import os
import cgi
def application(environ, start_response):
from webob import Response
form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ, keep_blank_values=True)
try:
fileitem = form['filename']
except KeyError:
fileitem = None
if fileitem is not None and fileitem.filename and fileitem.file:
# remove any relative paths to prevent directory traversal attacks
fn = os.path.basename(fileitem.filename)
# directory to upload to
fn = "/tmp/"+fn
with open(fn, 'w') as f:
# this upload is for text files
lines = fileitem.file.readlines()
for line in lines:
f.write(line)
f.close()
message = "Uploaded file: %s" % fn
else:
message = "Error: no file selected for upload"
html = '''
<html>
<body>
<p>%s
</body>
</html>
''' % message
response = Response(body = html,
content_type = "text/html",
charset = "utf8",
status = "200 OK")
return response(environ, start_response)
The only drawbacks to this implementation over the mod_python one is that I couldn't figure out a way to implement this solution in a single file. mod_python allowed me to call a function within a module and I can't figure out a way to do this with mod_wsgi. Also, during debugging it would have been nice to be able to log output to the same custom error_log file I had set up for mod_wsgi and I couldn't find a way of doing this, so had to log to the main Apache log file by using print statements. If someone has a way of doing this, I would be pleased to know a solution.

python mechanize handle two parameters with same name

I'm logging into a page where they oddly have a form input called login_email and two form inputs called login_password. I need to set the value of both but the straightforward call form['login_password'] throws an error:
File "/Library/Python/2.7/site-packages/mechanize/_form.py", line 3101, in find_control
return self._find_control(name, type, kind, id, label, predicate, nr)
File "/Library/Python/2.7/site-packages/mechanize/_form.py", line 3183, in _find_control
raise AmbiguityError("more than one control matching "+description)
mechanize._form.AmbiguityError: more than one control matching name 'login_password'
I just need to find a way to submit form['login_password'] = "Password" and form['login_password'] = "monkeybutler" at the same time. I'm not seeing a variable in the Browser object to change the POST data params.
Any suggestions?
Here's what I tried without success:
# Select the first (index zero) form
br.select_form(nr=0)
# Let's search
br.form['login_email'] = 'mommajane#gmail.com'
#my_fields = br.form.fields.select
#my_fields[0].login_password = "Password"
#my_fields[1].login_password = "123qwerty"
br.form['login_password']= ['Password','123qwerty']
br.submit()
If you are facing two fields with the same name, id and so on, you must use a little workaround, altough its not very clean
First I have defined a simple html file for that example since I did not know the URL you used:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>foo</title>
</head>
<body>
<h1>bar</h1>
<form action="input_text.htm">
<p>name:<br><input name="name" type="text" size="30" maxlength="30"></p>
<p>sec_name:<br><input name="sec_name" type="text" size="30" maxlength="40"></p>
<p>sec_name:<br><input name="sec_name" type="text" size="30" maxlength="40"></p>
</form>
</body>
</html>
Afterwards I was able to insert values into those fields quick and dirty by using this python code:
>>> import mechanize
>>> browser = mechanize.Browser()
>>> browser.open("file:///home/foo/index.html")
<response_seek_wrapper at 0x229a7e8 whose wrapped ...
>>> browser.select_form(nr=0)
>>> name = 'foo'
>>> for control in browser.form.controls:
... if control.name == 'sec_name':
... control.value = name
... name = 'bar'
...
>>> for control in browser.form.controls:
... print control
...
<TextControl(name=)>
<TextControl(sec_name=foo)>
<TextControl(sec_name=bar)>
>>>
It`s not nice but it works. Hope that helped.

Need to get the uploaded file to my local PC

I have created a test form which will ask users to enter a name and upload the image file:
<html lang="en">
<head>
<title>Testing image upload</title>
</head>
<body>
<form action="/services/upload" method="POST" enctype="multipart/form-data">
File Description: <input name='fdesc' type='text'><br>
File name: <input type="file" name="fname"><br>
<div><input type="submit"></div>
</form>
</body>
</html>
i need to get the file uploaded by the user and store it on my local PC. can this be done in python ? please let me know.
mod_python includes the FieldStorage class which allows you access to uploaded form data. In order to use it, you'd put something like the following in your Python script:
req.form = FieldStorage(req)
description = req.form['fdesc']
Since fdesc is a text input, description will be a string (more precisely, a StringField, which you can treat as a string).
file_field = req.form['fname']
Since fname is a file input, file_field will not be a string (or StringField), but rather a Field object which allows you access to the file data. The attribute file_field.file is a file-like object which you can use to read the file's contents, for example like so:
for line in file_field.file:
# process the line
You could use this to copy the file's data somewhere of your choosing, for example.
file_field.filename is the name of the file as provided by the client. Other useful attributes are listed in the documentation I linked to.
Maybie the minimal http cgi upload recipe and it's comments are helpful for you.
Hey David i got it working, i did it this way:
filename = request.FILES['fname']
destination = open('%s/%s'%(/tmp/,fileName), 'wb+')
for chunk in filename.chunks():
destination.write(chunk)
destination.close()
file = open('%s/%s'%(/tmp/,fileName),"rb").read()
Thanks for the help guys.

Categories