Get uploaded file name with Google App Engine Request class - python

I have an HTML form that allows file uploads. I need to send the uploaded file via email. I am following the Google appengine Request class example.
from google.appengine.ext import webapp
from google.appengine.api import mail
class MyRequestHandler(webapp.RequestHandler):
def get(self):
self.response.out.write('''
<html>
<body>
<form method="post">
<p>File: <input type="file" name="upload" /></p>
<p><input type="submit" /></p>
</form>
</body>
</html>
''')
def post(self):
uploaded_file = self.request.get("upload")
uploaded_name = "file.doc"
message = mail.EmailMessage(sender="Me <me#mydomain.com>",
subject="Email from me")
message.to = "Me <me#mydomain.com>"
message.body = "See attached file."
message.attachments = [(uploaded_name, uploaded_file)]
message.send()
The above code works. I need to replace the hardcoded file name (file.doc) in the message.attachments tuple with the actual name of the file uploaded. I have two questions:
How do I get the uploaded file name from the webapp Request class?
Where can I find the documentation that describes how to do this? I have done a bunch of searching and can not find reference material or examples.
UPDATE:
Looks like the solution is:
if uploaded_file != "":
uploaded_name = self.request.params["upload"].filename
...as posted
here.

Instead of going self.request.get("upload"), try examining the self.request.POST["upload"] dict.
Specifically, self.request.POST['upload'] will return a cgi.FieldStorage instance. The attribute you are looking for is filename. Also of interest would be mimetype - and of course data.
For a complete how-to on handling uploads, take a look at this ancient post over at Nick Johnson's blog.
For an under-the-hood look at how webapp2 request data works, consult this portion of webapp2 docs.

Related

Clarification - using Blobstore API to upload images/videos to Google Cloud Storage

I've been beating my head trying to figure out how to upload images/videos using Blobstore API to Google Cloud Storage for my App Engine Python web app. Here's is where I am so far:
I use the following code to create an action URL for my HTML form.
upload_url = create_upload_url('/upload', gs_bucket_name='bucket')
I use webapp2/Jinja templates and pass the upload_url into my HTML template.
<form action="{{upload_url}}" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" name="submit" value="SUBMIT">
</form>
I know that this will upload the file directly to Google Cloud Storage and then call my /upload handler with the blob keys.
I'm confused on what to do after this. This is my handler code for /upload :
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload = self.get_upload()[0]
blob_key = upload.key()
Is this the right way to get the blob_key when uploading images/videos to Google Cloud Storage?
The docs say to use blobstore.create_gs_key('/gs' + 'bucketname' + 'filename') to create a blob_key. Where does the filename come from? Should this code go into my post function for the upload handler that is called after the object is stored in GCS?
Also, from the Google docs, I'm confused as to what this code does or fits in to the picture:
with gcs.open(filename, 'w') as f:
f.write('abcde\n')
Check out this sample here that I just created for you in GitHub:
https://github.com/lucena/gae-upload-gcs
Please let me know if you have any questions on it.

can't get post parameter in multipart form

I have the following form shown in a modal of a bootstrap page
the aim is to upload a file with a hidden id
the application is based on Google App Engine, Python/Webapp2
<form action="someUrl" role="form" method="POST" enctype="multipart/form-data">
<input type="hidden" name="entityId" value="{{datastoreEntity.key.id()}}"/>
<input name="importFile" type="file" multiple>
<input type="text" class="form-control" readonly>
<input type="submit" name="submit" value="Import">
</form>
the problem is in the related RequestHandler (server side) where I can retrieve the file with
raw_file = self.request.POST.multi['importFile'].file
But I can't get the id (which is correctly generated by Jinja2 - checked in the page source). I have already tried with
self.request.get('entityId')
self.request.POST['entityId']
self.request.POST.multi['entityId']
I can't reproduce the problem as you relate it in the simplest of ways. I've copied that template into form.html, direct / to MainHandler, and net of the usual preparations (imports, jinja_environment with a FileLoader in the current dir, etc, I have):
class It(ndb.Model):
name = ndb.StringProperty()
class MainHandler(webapp2.RequestHandler):
def get(self):
dse = It(name="Willy")
dsek = dse.put()
datastoreEntity = dsek.get()
template = jinja_environment.get_template('form.html')
self.response.write(template.render(dict(
datastoreEntity=datastoreEntity,
)))
def post(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write(self.request.get('entityId'))
Visiting /, picking a file, and clicking the Import button, I see on my browser: 5066549580791808 -- which seems to be a typical datastore entity ID, as desired.
Please "interpolate" between this toy super-simplified version, and the no doubt much more complex things you're trying to do, and edit your Q to show us the very simplest way you can reproduce your problem, thanks!
Uploading via file inputs in App Engine requires that the app use a path that's obtained from the blobstore API. The doc for that is here. So you probably want to do something like
upload_url = blobstore.create_upload_url('/someUrl')
and then inject that into your template so that the form can use it
<form action="{{upload_url}}" ...
Once the file upload is complete, blobstore will redirect to /someUrl, where you process the rest of the form, including .file.

printing files in Google App Engine

I am trying to read and print a file in Google App Engine, but the code bellow seems unresponsive. I can upload the file, and my expectation was that it would just print the text, but it does nothing. I thought about adding a submit button, but I have no idea how to link submit with pythons printing. How can I get this to print on command?
I have seen the example provided by GAE here, but I would first like to keep it all on one page, and second I still don't understand how the submit calls that second page.
import webapp2
from google.appengine.ext.webapp import util
class MainPage(webapp2.RequestHandler):
#http://bukhantsov.org/2011/12/python-google-app-engine-calculator/
def get(self):
# build a list of operations
self.response.out.write("""<html>
<body>
<form action='/' method='get' autocomplete='off'>
<input type='file' name='file'/><br/>
#<input type='submit' name="test" value="submit">
</form>
</body>
</html>""")
file = self.request.get('file')
self.response.out.write(file)
app = webapp2.WSGIApplication([('/', MainPage)], debug=True)
def main():
util.run_wsgi_app(app)
if __name__ == '__main__':
main()
Your form is sent using the HTTP GET method, but for file uploads you need POST. Change it from:
method='get'
to:
method='post'
You will also need to handle POST requests in a different method. The POST body itself should be available as self.request.POST. So you end up with something like:
def post(self):
file = self.request.POST['file']
self.response.out.write(file)

Getting error: Could not find session for *blob_key*

Im trying to make a form where users can upload a file. And I have a form that takes the users arguments and posts them to the UploadHandler that is mapped to the url that is passed as parameter to the renderfunction. It works up to the point that it renders the form, but when submitting the file it redirects me to a blank page and the console shows me the error:
ERROR 2013-03-17 11:53:30,769 dev_appserver_blobstore.py:404] Could not find session for ahhkZXZ-a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA
INFO 2013-03-17 11:53:30,779 dev_appserver.py:3104] "POST /_ah/upload/ahhkZXZ- a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA HTTP/1.1" 404 -
Im still new at this but I can't seem to find whats going on. I understand that the mappings somehow f***s up, but still I don't understand why it doesn't redirect me to the correct handler (SummaryHandler)?
The class that serves the form for file upload:
class CreateHandler(BaseHandler):
def get(self):
self.render('create.html', upload_url = blobstore.create_upload_url('/upload'))
The html-form ('create.html'):
<h2 class="main-title">Upload a file!</h2>
<form action="{{upload_url}}" method="post" enctype="multipart/form-data">
<label>
<div>Upload:</div>
<input type="file" name="file" accept="application/pdf"><br>
</label>
<div class="error">
{{file_error}}
</div>
<br>
<input type="submit" name="submit" value="Submit">
</form>
The uploadhandler that takes the form args:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file')
blob_info = upload_files[0]
#validation of form-vars
have_error=False
file_data=dict()
if not (blob_info.content_type == 'application/pdf'):
file_data['file_error']="You can only upload files in pdf-format."
have_error=True
if have_error:
self.render('create.html', **file_data)
else:
#'parent' is just some stuff for organising the database.
#Not important in this context
the_file = A_File(parent = summary_key(),
file_key=str(blob_info.key()))
the_file.put()
if the_file:
self.redirect('/summary/%s' % str(the_file.key().id()))
else:
_error="Could not find the file"
self.render('welcome.html', error=_error)
The application handlers and their mappings:
app = webapp2.WSGIApplication([
(r'/', WelcomeHandler),
(r'/create', CreateHandler),
(r'/upload', UploadHandler),
(r'/summary/(\d+)', SummaryHandler) #I have not included this handler in the text
], debug=True)
It might be the fact that this session expired or it was used. The urls that created using create_upload_url have an expiration (I think 10 min). Before actually posting you might want to refresh that URL using JavaScript.
Also if for any reason the file was uploaded in the Blobstore you won't be able to use the same URL again, it is only good for one request (could have multiple files though).
In your example, try to remove all the file specific checks, try to upload something and check through the administrative console in the Datastore Viewer if you have any blobs or session keys. This can be accessed through this URL:
http://localhost:8080/_ah/admin/datastore

How to handle direct file upload with GAE/python?

Client side (javascript) uploads the application with XMLHttpRequest:
var req = new XMLHttpRequest();
req.open('POST', my_app_url, false);
req.setRequestHeader("Content-Length", req.length);
req.sendAsBinary(req.binary);
I use datastore on the server side (not blobstore).
How can I save uploaded file to the datastore? I've found that ServletFileUpload can be used with Java. But how to do the same with Python?
You should use self.request.body
class YourUploadHandler(webapp.RequestHandler):
def post(self):
your_binary_content = self.request.body
If you mean on the appengine side, you just have to have a blobproperty. So something like...
class SomeEntity(db.Model):
file_data = db.BlobProperty()
class AddData(webapp.RequestHandler)
def post(self):
data = self.request.get("filedata")
e = SomeEntity(file_data = db.Blob(data))
e.put()
As a note, I'm not sure if the code you posted above to send the request is correct, but you can upload the file with a simple html form, something like this:
<form action="/url_to_adddata_handler/" method="post">
<input type="file" name="filedata">
<input type="submit" value="Submit">
</form>

Categories