def upload(request): """View used when support for Nginx Upload Progress module has been disabled. This view includes 3 steps: 1. We change the "state" column in the upload table so that our AJAX-called 'progress' view sees that the upload has started and that the temporary file can be stat'ed. 2. We copy the data from the POST request to a temporary file. This step is long, obviously, as we copy data as we receive it from the browser. While we are copying data, the browser will call the 'progress' view to update a progress bar. 3. Once the file has been copied, we rename it to its final filename and redirect the browser to the success page. """ # We pass the 'file_id' in the query string as a GET parameter. If # we read it from the POSTed data, WebOb would read all POSTed # data, which has various features and traps (like setting the # "Content-Length" header to 0) that we do not need since we are # going to read the data ourselves anyway. file_id = request.GET['X-Progress-ID'] input_file, file_size, filename = get_file_from_request(request) session = DBSession() u = session.query(Upload).filter_by(id=file_id).one() upload_dir = request.registry.settings['poulda.upload_dir'] user_id = authenticated_userid(request) # We use a temporary path to detect unfinished uploads (post # mortem, not in the application itself). path = os.path.join(upload_dir, '_'.join((user_id, file_id))) u.tmp_path = path u.started = int(time.time()) u.size = file_size u.state = u'uploading' session.flush() # We need to commit the transaction so that changes to the Upload # object can be seen by the other threads (which will serve the # 'progress' JSON view called by the upload page). transaction.commit() with open(path, 'w') as output: # We must read only 'file_size' bytes from the 'input_file', # not all of it since it also contains the MIME boundary. copy_to_file(input_file, file_size, output) final_path = filename[1 + filename.rfind(os.sep):] final_path = os.path.join(upload_dir, final_path) os.rename(path, final_path) session = DBSession() u = session.query(Upload).filter_by(id=file_id).one() u.state = u'done' u.final_path = unicode(final_path, 'utf-8') return HTTPFound(location='success')
def progress(request): """View used only when the Nginx Upload Progress support has been disabled. When the Nginx Upload Progress support is enabled, this view is never called at all, since the Nginx module takes care of returning progress information. """ file_id = request.GET['X-Progress-ID'] session = DBSession() u = session.query(Upload).filter_by(id=file_id).one() data = {'state': u.state} if u.state == 'uploading': if not os.path.exists(u.tmp_path): # The temporary file has not been created yet or it has # already been renamed. We return 0 in both case, the # front-end code will know what to do. received = 0 else: received = os.stat(u.tmp_path).st_size data.update({'size': u.size, 'received': received}) return data