witten
/
luminotes
Archived
1
0
Fork 0

Rewrote controller.Files.upload_file() not to use a CherryPy-2.1-style file upload filter.

Now releasing session lock before streaming the file to prevent session deadlock in the event of a cancelled upload.
This commit is contained in:
Dan Helfman 2008-02-04 20:06:02 +00:00
parent 362a125b1b
commit 04f86f05a6
4 changed files with 20 additions and 26 deletions

View File

@ -1,4 +1,5 @@
import cherrypy import cherrypy
import types
# module-level variable that, when set to a view, overrides the view for all exposed methods. used # module-level variable that, when set to a view, overrides the view for all exposed methods. used
# by unit tests # by unit tests
@ -64,7 +65,7 @@ def expose( view = None, rss = None ):
result = dict( error = u"An error occurred when processing your request. Please try again or contact support." ) result = dict( error = u"An error occurred when processing your request. Please try again or contact support." )
# if the result is a generator, it's streaming data, so just let CherryPy handle it # if the result is a generator, it's streaming data, so just let CherryPy handle it
if hasattr( result, "gi_running" ): if isinstance( result, types.GeneratorType ):
return result return result
redirect = result.get( u"redirect", None ) redirect = result.get( u"redirect", None )

View File

@ -1,6 +1,5 @@
import cgi import cgi
import cherrypy import cherrypy
from cherrypy.filters import basefilter
from Expose import expose from Expose import expose
from Validate import validate from Validate import validate
from Database import Valid_id from Database import Valid_id
@ -37,21 +36,7 @@ class Upload_error( Exception ):
) )
class File_upload_filter( basefilter.BaseFilter ):
def before_request_body( self ):
if cherrypy.request.path != "/files/upload_file":
return
if cherrypy.request.method != "POST":
raise Upload_error()
# tell CherryPy not to parse the POST data itself for this URL
cherrypy.request.processRequestBody = False
class Files( object ): class Files( object ):
_cpFilterList = [ File_upload_filter() ]
""" """
Controller for dealing with uploaded files, corresponding to the "/files" URL. Controller for dealing with uploaded files, corresponding to the "/files" URL.
""" """
@ -94,20 +79,31 @@ class Files( object ):
@strongly_expire @strongly_expire
@grab_user_id @grab_user_id
@validate( @validate(
upload = (),
notebook_id = Valid_id(),
note_id = Valid_id(),
user_id = Valid_id( none_okay = True ), user_id = Valid_id( none_okay = True ),
) )
def upload_file( self, user_id ): def upload_file( self, upload, notebook_id, note_id, user_id ):
""" """
Upload a file from the client for attachment to a particular note. Upload a file from the client for attachment to a particular note.
@type upload: cgi.FieldStorage
@param upload: file handle to uploaded file
@type notebook_id: unicode @type notebook_id: unicode
@param notebook_id: id of the notebook that the upload is to @param notebook_id: id of the notebook that the upload is to
@type note_id: unicode @type note_id: unicode
@param note_id: id of the note that the upload is to @param note_id: id of the note that the upload is to
@raise Access_error: the current user doesn't have access to the given notebook or note @raise Access_error: the current user doesn't have access to the given notebook or note
@raise Upload_error: an error occurred when processing the uploaded file
@type user_id: unicode or NoneType
@param user_id: id of current logged-in user (if any)
@rtype: unicode @rtype: unicode
@return: rendered HTML page @return: rendered HTML page
""" """
if not self.__users.check_access( user_id, notebook_id ):
raise Access_error()
cherrypy.server.max_request_body_size = 0 # remove file size limit of 100 MB cherrypy.server.max_request_body_size = 0 # remove file size limit of 100 MB
cherrypy.response.timeout = 3600 # increase upload timeout to one hour (default is 5 min) cherrypy.response.timeout = 3600 # increase upload timeout to one hour (default is 5 min)
cherrypy.server.socket_timeout = 60 # increase socket timeout to one minute (default is 10 sec) cherrypy.server.socket_timeout = 60 # increase socket timeout to one minute (default is 10 sec)
@ -125,15 +121,8 @@ class Files( object ):
if file_size <= 0: if file_size <= 0:
raise Upload_error() raise Upload_error()
parsed_form = cgi.FieldStorage( fp = cherrypy.request.rfile, headers = headers, environ = { "REQUEST_METHOD": "POST" }, keep_blank_values = 1)
upload = parsed_form[ u"file" ]
notebook_id = parsed_form.getvalue( u"notebook_id" )
note_id = parsed_form.getvalue( u"note_id" )
filename = upload.filename.strip() filename = upload.filename.strip()
if not self.__users.check_access( user_id, notebook_id ):
raise Access_error()
def process_upload(): def process_upload():
""" """
Process the file upload while streaming a progress meter as it uploads. Process the file upload while streaming a progress meter as it uploads.
@ -222,4 +211,8 @@ class Files( object ):
upload.file.close() upload.file.close()
cherrypy.request.rfile.close() cherrypy.request.rfile.close()
# release the session lock before beginning the upload, because if the upload is cancelled
# before it's done, the lock won't be released
cherrypy.session.release_lock()
return process_upload() return process_upload()

View File

@ -2258,7 +2258,7 @@ Upload_pulldown.prototype.init_frame = function () {
withDocument( doc, function () { withDocument( doc, function () {
connect( "upload_button", "onclick", function ( event ) { connect( "upload_button", "onclick", function ( event ) {
withDocument( doc, function () { withDocument( doc, function () {
self.upload_started( getElement( "file" ).value ); self.upload_started( getElement( "upload" ).value );
} ); } );
} ); } );
} ); } );

View File

@ -12,7 +12,7 @@ class Upload_page( Html ):
Body( Body(
Form( Form(
Span( u"attach file: ", class_ = u"field_label" ), Span( u"attach file: ", class_ = u"field_label" ),
Input( type = u"file", id = u"file", name = u"file", class_ = "text_field", size = u"30" ), Input( type = u"file", id = u"upload", name = u"upload", class_ = "text_field", size = u"30" ),
Input( type = u"submit", id = u"upload_button", class_ = u"button", value = u"upload" ), Input( type = u"submit", id = u"upload_button", class_ = u"button", value = u"upload" ),
Input( type = u"hidden", id = u"notebook_id", name = u"notebook_id", value = notebook_id ), Input( type = u"hidden", id = u"notebook_id", name = u"notebook_id", value = notebook_id ),
Input( type = u"hidden", id = u"note_id", name = u"note_id", value = note_id ), Input( type = u"hidden", id = u"note_id", name = u"note_id", value = note_id ),