Django

Code

Ticket #2070 (closed: fixed)

Opened 3 years ago

Last modified 4 days ago

Large streaming uploads

Reported by: oyvind.saltvik@gmail.com Assigned to: jacob
Milestone: 1.0 beta Component: HTTP handling
Version: SVN Keywords: streaming, upload, large, sprintsept14, feature
Cc: dsalvetti@trapeze.com, matthias@urlichs.de, oliver@obeattie.com, Maniac@SoftwareManiacs.Org, nesh@studio-quattro.com, aenor.realm@gmail.com, gary.wilson@gmail.com, serialx.net@gmail.com, wonlay@gmail.com, lurker86@gmail.com, shaun@cranedata.us, antonio.mele@gmail.com, herbert.poul@gmail.com, gabor@nekomancer.net, koebbe@wustl.edu, axiak@mit.edu, jm.bugtracking@gmail.com, moritz.angermann@gmail.com, django@versea.be, brosner@gmail.com, root.lastnode@hccnet.nl, akaihola+djtick@ambitone.com, sam@robots.org.uk, trey@ktrl.com, klaus.blindert@web.de, ville@unessa.net, reza@zeerak.ir, registrations@eugenemorozov.name, calvin@debian.org, works@ksu.edu, andrewbadr.etc@gmail.com, uptimebox@gmail.com, drfarina@acm.org, beau@hartshorne.ca, giuliani.v@gmail.com, prigun@gmail.com, zerok@zerokspot.com, johannes.beigel@brainbot.com, david@zettazebra.com, django@julienphalip.com, mocksoul@gmail.com, schlaber@gmail.com, danpoe@tschitschereengreen.com, django@hangya.net, django-2070@codef0x.org, clintecker@gmail.com Triage Stage: Accepted
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

Based on code from ticket 1448.

Test for large file upload

from django.db import models

# Create your models here.
class FileList(models.Model):
    name = models.CharField(maxlength=255)
    email = models.EmailField()

    class Admin:
        pass

class AFile(models.Model):
    descr = models.CharField(maxlength=255,core=True)
    file = models.FileField(upload_to='files')
    inlist = models.ForeignKey(FileList,edit_inline=models.STACKED)

And two 500 megabyte files for upload.

Works with the patch with +20 mb above average memory and minimal cpu load usage by the httpd thread, file upload successful.

Without the patch httpd rages to +140 mb above average memory usage and over 60% cpu usage and fails miserably in the end.

Attachments

3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id.diff (16.7 kB) - added by [530] on 08/26/06 21:43:49.
Now using X_PROGRESS_ID instead of X-Progress-Id, accepts any lower/uppercase/ - / _ /prefix variant
3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id_windowsfix.diff (16.9 kB) - added by [Cha0S] on 09/20/06 08:58:51.
windows temporary file locking fix
modpyton-ok-needs-fcgi-testing.3.diff (28.6 kB) - added by Øyvind Saltvik <oyvind.saltvik@gmail.com> on 02/02/07 14:50:09.
did not work without middleware, fixed
4459-streaming-file-upload.diff (25.9 kB) - added by Joakim Sernbrant <serbaut@gmail.com> on 02/04/07 18:54:46.
Simplified streaming uploads
4459-streaming-file-upload.2.diff (26.7 kB) - added by Joakim Sernbrant <serbaut@gmail.com> on 02/05/07 11:08:37.
Added FILE_UPLOAD_MIN_SIZE (default 100kb) to define minimum request size for streaming to disk. Propage exeptions. I'm not too happy with the names of the settings anymore :/
5065-streaming_file_upload_with_shutils.diff (26.6 kB) - added by axiak@mit.edu on 04/24/07 21:58:59.
I've updated the patch to include the shutils command and to work with [5065]. Please check to see if it works.
5065-streaming_file_upload_with_shutils_2.diff (27.5 kB) - added by axiak@mit.edu on 04/24/07 22:33:16.
Works in [5065], renamed settings variable, uses global settings, defaults STREAMING_MIN_POST_SIZE to .5MB (please test though!)
5070-streaming-file-upload.diff (26.7 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 04/25/07 03:36:05.
Updated to trunk, without changes
5078_streaming_file_upload_with_shutils_and_fallbacks.diff (28.1 kB) - added by Michael Axiak <axiak@mit.edu> on 04/25/07 11:32:17.
Usese shutils, but falls back.
5078_streaming_file_upload_with_shutils_and_fallbacks_2.diff (28.2 kB) - added by Michael Axiak <axiak@mit.edu> on 04/25/07 11:35:28.
Usese shutils, but falls back, this time it deletes the tmp file even when it falls back.
5078-streaming_file_upload_with_shutils_and_chunked_fallbacks.diff (28.4 kB) - added by Michael Axiak <axiak@mit.edu> on 04/25/07 13:07:36.
This file uses python fallbacks, but using chunks to avoid loading the entire file into memory.
5079-streaming_file_upload_with_safe_file_move.diff (29.1 kB) - added by Michael Axiak <axiak@mit.edu> on 04/25/07 19:12:36.
Cleaned it up a bit. Moved file_move_safe into django.utils in case it should be used in future endeavors.
5089-streaming_file_upload_with_safe_file_move.diff (28.0 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 04/26/07 08:18:14.
Added missing else
5089-streaming_file_upload_with_safe_file_move.2.diff (29.1 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 04/26/07 09:28:10.
Forgot to add django/utils/file.py
5099_patch_for_streaming_uploads.diff (30.8 kB) - added by Michael Axiak <axiak@mit.edu> on 04/26/07 13:05:16.
Uses multiple mechanisms for determining the progress id.
5100_upload_request_meta.diff (33.8 kB) - added by Michael Axiak <axiak@mit.edu> on 04/26/07 18:22:31.
This isn't important enough to get its own property in request...request.META now contains 'UPLOAD_PROGRESS_ID'
5100_forgot_to_revert_base.diff (32.5 kB) - added by Michael Axiak <axiak@mit.edu> on 04/26/07 18:30:25.
I included a bit much in that last patch.
5100_file_upload_core.diff (34.1 kB) - added by Michael Axiak <axiak@mit.edu> on 04/26/07 20:15:08.
Meant to be cleaner, especially in light of #4165
5100_file_upload_core_with_middleware_hooks.diff (37.7 kB) - added by Michael Axiak <axiak@mit.edu> on 04/27/07 00:28:52.
Added middleware hooks
5100_file_upload_core_with_middleware_hooks_2.diff (37.7 kB) - added by Michael Axiak <axiak@mit.edu> on 04/27/07 00:30:21.
Added middleware hooks...this is better.
5100_file_upload_core_with_middleware_hooks_3.diff (41.8 kB) - added by Michael Axiak <axiak@mit.edu> on 04/27/07 00:48:16.
Apparently I don't know how not to lose files.
5100_file_upload_core_with_middleware_hooks_4.diff (38.9 kB) - added by Michael Axiak <axiak@mit.edu> on 04/27/07 00:55:13.
Apparently I don't know how not to lose files.
5113_file_upload_core_with_middleware_hooks.diff (39.1 kB) - added by Michael Axiak <axiak@mit.edu> on 04/27/07 12:12:24.
Fixes WSGI bug.
5116_streaming_upload_fixed_middleware_append.diff (35.4 kB) - added by Michael Axiak <axiak@mit.edu> on 04/28/07 00:56:40.
Fixed bug where .append(0, func) was called.
5116_streaming_upload_fixed_middleware_append_2.diff (42.4 kB) - added by Michael Axiak <axiak@mit.edu> on 04/28/07 00:57:51.
Fixed bug where .append(0, func) was called with the files this time.
5126_file_upload_wsgi_tested.diff (41.3 kB) - added by Michael Axiak <axiak@mit.edu> on 04/28/07 21:18:52.
Tested with WSGI and made a few changes.
5126_file_uploads_latest_patch.diff (39.4 kB) - added by Michael Axiak <axiak@mit.edu> on 04/29/07 03:10:33.
Added 'state':'starting' to be more like mod_uploadprogress.
5127_file_uploads_no_streaming_fixed.diff (39.2 kB) - added by SmileyChris on 04/29/07 21:42:39.
There was an error uploading large files with streaming turned off
5313_updated_file_progress.diff (36.7 kB) - added by Michael Axiak <axiak@mit.edu> on 05/22/07 03:20:55.
Removed some unneeded things. No file progress tracking by default.
5343_1_streaming_file_upload.diff (38.4 kB) - added by Michael Axiak <axiak@mit.edu> on 05/25/07 22:56:34.
Newer, cleaner version of file upload script
5343_streaming_file_upload_no_assert.diff (39.3 kB) - added by Michael Axiak <axiak@mit.edu> on 05/25/07 22:58:14.
Newer, cleaner version of file upload script (doh! no random commented assert)
5343_streaming_file_upload_best.diff (38.4 kB) - added by Michael Axiak <axiak@mit.edu> on 05/25/07 23:01:15.
Sorry about that -- this one is the better one.
5343_cleaned_streaming_file_upload.diff (37.4 kB) - added by Michael Axiak <axiak@mit.edu> on 05/25/07 23:17:18.
It's amazing what Trac helps you see.
5343_cleaned_streaming_file_upload_tweaked.diff (37.3 kB) - added by Michael Axiak <axiak@mit.edu> on 05/26/07 00:49:31.
Made a subtle fix after testing with #4165.
5343_cleaned_streaming_file_upload_tweaked2.diff (37.3 kB) - added by klaus.blindert@web.de on 07/12/07 07:57:28.
Fixed microscopic bug in an error path
5722.diff (26.4 kB) - added by simonbun <simonbun@versea.be> on 07/19/07 04:22:19.
Updated patch against r5722
5722.2.diff (22.2 kB) - added by simonbun <simonbun@versea.be> on 07/19/07 04:30:31.
Sorry, that last one patch also included #4165. This is the correct one.
5724_streaming_file_upload.diff (37.8 kB) - added by Brian Rosner <brosner@gmail.com> on 07/19/07 17:43:06.
updated to r5724 (previous patch was missing files too)
5820_streaming_file_upload.diff (37.8 kB) - added by Brian Rosner <brosner@gmail.com> on 08/06/07 11:07:06.
updated to r5820
6469_streaming_file_upload.diff (38.5 kB) - added by Faheem Mitha <faheem@email.unc.edu> on 10/10/07 16:56:05.
6525_all_tests_pass.diff (39.6 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 10/17/07 05:08:02.
Fixed unicode in POST issues, added django.http.utils, moved str_to_unicode there
6603_all_tests_pass_uploadedfile_wrapper_fixed.diff (42.1 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 10/26/07 05:22:30.
Fixed a problem with UploadedFile? wrapper and making sure content is not read in Fie/ImageField
6652_isValidImage_using_tmpfilename.diff (42.7 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 11/05/07 09:34:16.
made the isValidImage validator try the tempfile before reading content
6654_fixed_tests_and_file_clean.diff (45.0 kB) - added by Øyvind Saltvik <oyvind@saltvik.no> on 11/06/07 09:09:37.
diff did not apply cleanly, fixed
streaming.6710.patch (45.5 kB) - added by Faheem Mitha <faheem@email.unc.edu> on 11/25/07 14:58:31.
Updated streaming patch to trunk rev 6710.
streaming.7092.patch (44.8 kB) - added by egon on 02/06/08 04:44:47.
Made it apply cleanly to rev. 7092
streaming.7092.patch.partial_tests_fix (48.6 kB) - added by faheem on 02/08/08 14:55:41.
Slightly modified version of streaming.7092.patch with more tests passing.
streaming.7106.patch (48.0 kB) - added by egon on 02/12/08 03:17:23.
Passes al tests.
ticket2070_rev7277.diff (48.1 kB) - added by axiak on 03/17/08 22:33:08.
Same patch applied to @7277
2070_revision7339_uploadhandling.diff (38.8 kB) - added by axiak on 03/20/08 15:04:37.
NEW Upload handling for revision 7339
2070_revision7339_formhandling.diff (11.4 kB) - added by axiak on 03/20/08 15:05:47.
NEW Form handling for uploaded files for revision 7339
2070_revision7351_latest.diff (67.7 kB) - added by axiak on 03/22/08 22:49:15.
Latest patch for 2070...includes everything (see comment)
2070_latest7354.diff (68.8 kB) - added by axiak on 03/23/08 15:48:28.
The latest #2070 patch. Small changes...see comment.
2070_revision7359.diff (76.9 kB) - added by axiak on 03/24/08 14:46:38.
New diff...some style changes and new documentation.
2070_revision7363.diff (93.2 kB) - added by axiak on 03/26/08 01:16:43.
Altered documentation to be more approachable.
2070_revision7363.2.diff (94.0 kB) - added by axiak on 03/26/08 08:28:36.
Slightly newer...handles exhaustion a little bit differently.
2070_revision7377.diff (95.3 kB) - added by axiak on 03/29/08 01:11:04.
New 2070 patch...includes fixes to the parser in addition to the newforms fix caught by EricB as well as better docs.
2070_revision7396.diff (98.6 kB) - added by axiak on 03/31/08 19:50:57.
Better patch...not sure how the newforms code in the previous patch was the way it was.
2070_revision7388.diff (98.6 kB) - added by axiak on 04/01/08 18:14:57.
new patch that fixes error with instantiating the upload handlers, thanks Eric!
2070_revision7484.diff (99.2 kB) - added by axiak on 04/27/08 17:54:31.
Updated diff per Jacob's requests and useful feedback.
2070_revision7484.2.diff (99.3 kB) - added by axiak on 04/27/08 23:01:43.
Fixed file_size bug with memory handling.
2070_revision7599.diff (45.6 kB) - added by leahculver on 06/09/08 03:22:20.
Updated patch to r7599
2070-r7695.patch (100.6 kB) - added by jacob on 06/18/08 15:47:42.
Streaming uploads patch updated to [7695] and reviewed.
2070-r7728.patch (114.5 kB) - added by jacob on 06/26/08 14:02:55.
"release candidate" patch

Change History

06/02/06 07:54:01 changed by oyvind.saltvik@gmail.com

  • severity changed from normal to major.

Should there be a setting to enable/disable this?

Needs testing on python 2.3.

06/02/06 08:07:28 changed by ubernostrum

  • priority changed from high to normal.
  • status changed from new to closed.
  • resolution set to duplicate.
  • severity changed from major to normal.

This is a duplicate of #1484.

06/02/06 08:09:49 changed by ubernostrum

  • status changed from closed to reopened.
  • resolution deleted.

Or is it? Leaving it open until someone who knows more about it can sort this out.

06/09/06 06:05:28 changed by [530]

Detailed description of patch.

Based of the #1448 patch that i experienced trouble with using edit inline and large files.

Changes from trunk:

  • Added STREAMING_UPLOADS=False to global_settings
  • Added parse_streaming_file_upload it http/__init__.py
  • Made changes to modpython and wsgi in handlers to use parse_streaming_file_upload
  • Subclassed cgi.FieldStorage
    • overriding of make_file to use NamedTemporaryFile
    • overriding of bouandry parsers to make readline read in 64k chunks to avoid reading extremely long lines into memory
  • Subclassed rfc822.Message
    • overriding of bouandry parsers to make readline read in 64k chunks to avoid reading extremely long lines into memory
  • Made FileField validate upload using tempfile size
  • Made save_file in db/models/fields/__init__.py pass temporary file name to _save_FIELD_file instead of file contents
  • Made _save_FIELD_file copy temporary file instead of write file from memory

06/09/06 10:59:26 changed by [530]

Error in previous post.

Based of the #1448 patch that i experienced trouble with using edit inline and large files.

Should be.

Based of the #1484 patch that i experienced trouble with using edit inline and large files.

06/12/06 02:33:52 changed by Matias Hermanrud Fjeld <mhf@hex.no>

This patch uses the rfc822 module. The Python docs says the following about rfc822:

  Deprecated since release 2.3. The email package should be used in preference to the rfc822 module.
  This module is present only to maintain backward compatibility.

06/12/06 06:09:25 changed by Home

  • type deleted.

06/16/06 05:54:53 changed by [530]

  • type set to defect.

The problem with the email package is that it reads the entire file into memory. Django supports python 2.3 anyway.

07/09/06 17:37:53 changed by [530]

to use monitor from a view

def progress_view(request):
    
    import pickle

    sessionid = request.COOKIES.get('sessionid', None)
    progress = pickle.load(open('/tmp/'+sessionid, 'r'))

Accessible variables

progress.fullength - full request byte lenght progress.at_status - current bytes read progress.at_file - current file read progress.file_status - current file bytes read

07/19/06 17:35:35 changed by bnewbold@mit.edu

Had a problem on FreeBSD with my /tmp being on a seperate filesystem: os.rename threw an "Invalid cross-device link" error. The fix was to edit django/db/models/base.py, "import shutils" at the top and change the one instance of "os.rename" to "shutils.move". Works fine now... shutils came default with python2.4 I don't know about other releases, or if it works on other OSs

(follow-up: ↓ 107 ) 07/19/06 17:41:17 changed by adrian

In reference to bnewbold's comment above, it's the shutil module, and it looks like it's been around since at least Python 2.3. (Django supports Python 2.3 and up.)

07/22/06 04:27:15 changed by [Cha0S]

to make it work on windows make the following changes in UploadStateKeeper? class:

class UploadStateKeeper:

    def __init__(self, identifier, fullength=None):

        self.identifier = identifier

        if fullength:
            self.state = UploadState(self.identifier, fullength)

        else:
            import pickle
            self.state = pickle.load(self.get_status_path(), 'r')

    def get_status_path(self):
        from tempfile import gettempdir
        return os.path.join(gettempdir(), self.identifier)

    def set_status(self, filename, addlen):
        self.state.set_status(filename, addlen)
        import pickle
        pickle.dump(self.state,open(self.get_status_path(), 'w'))

    def get_state(self):
        return self.state

07/27/06 17:20:40 changed by jacob

Can anyone tell me the differences between the latest patch on this ticket and the latest one on #1484? Are these two tickets/patches duplicates or complements?

07/29/06 12:26:21 changed by bahamut@macstorm.org

  • type changed from defect to enhancement.

This ticket is the continuation of #1484.

07/29/06 13:08:22 changed by jacob

Sorry if I'm being dense, but does that mean that #1484 should be closed in favor of this one? I'm trying to figure out which patch(es) need to be applied here.

07/29/06 15:33:08 changed by bahamut@macstorm.org

  • keywords set to timezone, time zone, UTC.

In my opinion, yes #1484 should be closed. The only patch you have to apply is the latest one in this ticket.

It may help to svn up -r 3358 before you apply the patch, or you can try at the current HEAD and see what goes.

07/29/06 23:19:08 changed by jacob

Thanks.

07/31/06 16:21:38 changed by jacob

  • owner changed from adrian to jacob.
  • status changed from reopened to new.

Right, I've finally had a chance to play with this patch. It's a big one, so I want to make sure I understand what's going on before I check it in. So apologies if I'm being dense, but I gotta understand this stuff:

  • I'm confused about why you needed to override Message.readline} just to change the argument to self.fp.readline() (django/http/init.py, line 105). Is the Python implementation broken in some way? What happens if you take that bit out?

  • Pretty much the same question for the FieldStorage? subclass. This is a *lot* of duplicated code from the builtin cgi module, and it gives me pause. Can you explain why you need to do this (and perhaps think about if there's another way?)

  • The upload status info stored in /tmp is pretty awesome. However, it doesn't seem to work all the time for me; while uploading a big file if I keep cat'ing that /tmp file it's often just empty. Seems something keeps emptying it out? Also, using the session ID seems like it would cause issues if I tried to upload multiple files at the same time (a very real possibility if I'm uploading really big-ass things).

  • Related to that, setting os.envrion['SESSIONID'] (done in both the WSGI and the mod_python handler) seems very leaky. We've had bugs before with this type of env monkeybusiness... Could you use a threadlocal instead?

I should end, however, by saying that the behavior of this patch rocks. Being able to upload a 2 GB file without locking my server is super-nice, and the status stuff absolutely kicks ass (I think I already said that). I'm pretty sure I can clean up all the stuff I just bitched about, but I've got a pretty long todo.txt and if you can take care of these problems and/or answer my questions, I'll check this in PDQ.

07/31/06 16:21:45 changed by jacob

  • status changed from new to assigned.

08/01/06 08:38:26 changed by [530]

  • If that bit is taken out memory usage soars because most browsers do not use newlines when sending files so it actually reads to EOF. Is it possible to override self.fp.readline() to call self.fp.readline(64000)? Then most of this code would be removed.
  • The upload status is a bit tricy, I need to override FieldStorage? to store the status. But since FieldStorage? is not a extended class of object there is no super(FieldStorage?, self).read_lines_to_outerboundary() causing repetition of code.
  • Don't know how multiple uploads can be solved since it would require multiple id's, maybe use sessions instead if possible. Never experienced the emptiying if the temp file. Does this happen with both mod_python and wsgi?
  • About setting os.environ, the mod_python-handler does not set it, but wsgi does, need to fix that one instead of messing with threadlocal , this may be why the file is emptied if it only happens with wsgi.

08/02/06 11:39:19 changed by anonymous

  • cc set to Maniac@SoftwareManiacs.Org.

08/02/06 12:10:28 changed by anonymous

Jacob, after reading your questions I'd like to share here my knowledge as an author of original patch in #1484.

  • About readline(N). Actually browsers do send newlines when sending binary files because newlines (byte 0x0A) are present in binary files also. In theory 0x0A in a binary body can be rare enough for readline to read very large chunks in memory. However in practice 0x0A are present often enough (I'm talking from the experience of two services working mainly with big jpegs, mp3 and zips). This is, btw, why my original patch in #1484 didn't include the boundary in readline. The downside of replacing readlines in FieldStorage?'s code with readline(N) is that the base class doesn't provide this for overriding so you have to actually rewrite one-to-one pretty large chunks of code.
  • To my liking storing upload info in files is not very nice. I mentioned my idea in django-developers about this that it's better be a hook for the user code (in middleware style) that is called from the handler accepting uploaded data. This is more flexible because a user can store this progress not in /tmp but wherever he likes and do more interesting stuff then just measuring progress. For example closing the upload if the file is too big for a server.
  • One more thing that we've already discussed with Oyvind in an old ticket that his patch alters both HTTP handlers and admin app. The old patch was only for HTTP and my point is that these things should be separate because it's much simpler to review and apply.

Hope my comment was useful!

08/02/06 12:14:42 changed by anonymous

One more nit... Oyvin what was the reason to use rfc822? It's a new thing in your variant of the old patch and I just don't get why it's needed :-)

08/02/06 15:55:34 changed by [530]

  • When uploading two 500mb iso's, it broke down constantly with a memoryerror when not setting a buffer size, everything worked fine when a bufsize was added.
  • About upload storage I do agree about using a hook instead.
  • Rfc822 is still used by FieldStorage?, now hidden from view. I just don not think of setting bufsize in make_file instead of passing it to readline (if that works). Have not had time to test yet.
  • Agree that reviewing is easier. But it is nice for easy testing via the admin to see why django is the right tool for the massive job. :)

08/03/06 04:09:20 changed by anonymous

Ignore my patch 3358-streaming-file_with_settings_and_progressmonitor.3.diff, adding bufsize does not affect readline. Also it seems chunked reads are required for storing the state. Don't see any easy way to make this less code yet. I'll add a new patch with just the WSGI fix.

08/04/06 10:46:07 changed by [530]

I was planning to make a middleware for a progressmonitor hook, but not quite sure when middleware is applied and how to pass this hook from the middleware to FieldStorage?. Any help to clarify/fix this would be great.

Also if somenoe could test this successfully using WSGI it would be great.

(follow-up: ↓ 125 ) 08/04/06 12:08:31 changed by Maniac <Maniac@SoftwareManiacs.Org>

My idea of a hook is not fit into current middleware, I just wanted to retain the style. So to clarify I see it like this.

This can be a separate set of hooks similar to MIDDLEWARE_CLASSES or it can be done in middleware classes themselves. To do the latter we will specifiy that in addition to process_request, _response, _view and _exception methods a middleware can define a new kind of method: process_upload(request, data).

parse_file_upload then would search for these middleware methods just as http requests do in modpython.py and wsgi.py. The list of methods is passed to a FieldStorage? decendant that will call them each time it's about to write a chunk of data to the target stream.

This is it... Sorry I can't come with the actual code: pretty busy rolling out my current project.

08/05/06 08:49:04 changed by Todd O'Bryan

I figured the last attachment was supposed to be a diff and tried to upload it with the right extension. Unfortunately, '.py' got added and now I don't know how to get rid of the attachment.

Sorry for the clutter.

08/05/06 15:47:27 changed by [530]

Seems wsgi can't do streaming uploads this way, but ligthttpd 1.5 will chunk the uploads and has a mod_uploadprogress that serializes to JSON. Will try adapt this patch to serialize the same way so it wont matter if you use this streaming uploads patch or mod_uploadprogress.

08/05/06 16:16:26 changed by Maniac@SoftwareManiacs.Org

Why do you say wsgi can't do this? It gives environwsgi.input? to the handler that is a file-like object with uploading data. Handler then can read it in chunks (it actually does this in parse_file_upload).

08/05/06 21:13:11 changed by [530]

WSGI may, but it seems like lighthttpd does not pass anything to wsgi before the request is done. I may be wrong, but I have no way of debugging properly.

08/12/06 10:16:55 changed by [530]

Bug in last patch, did not manage to override readline to use a buffer size. If this cannot be done, code must be repeated.

08/13/06 14:19:45 changed by [530]

Wsgi users using lighttpd should use mod_uploadprogress instead. There is some kind of delay before the upload state is updated using wsgi, the same does not happen on mod_python. Is this a issue with my code, the fastcgi/scgi spec or lighttpd's implementation? Can someone test this with fastcgi/scgi on apache?

08/13/06 14:30:06 changed by [530]

GET identifer for upload state.

http://foo.com?bar saves json data in /tmp/bar

For javascript, making a /progress/ url and view see http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back

Makes it easy to add a admin js option to a model to enable upload state in admin.

08/13/06 18:44:34 changed by toddobryan

I just provided a patch which steals liberally from Oyvind, but decouples the progress monitor from the rest of the file upload behavior.

In a nutshell, if you don't want to read uploads into memory, you set request.save_file_class to the class of the kind of file-like object you'd like uploads routed to. A sample middleware class is provided which uses a temp file.

I haven't looked at Oyvind's latest code, but my guess is that we could easily move the upload monitor from the guts of Django by providing a file-like object class that keeps track of how much stuff it has received from the POST data stream.

Comments welcome. (But be nice; it's my first patch.)

08/13/06 19:06:41 changed by [530]

To toddobryan, I made some fatal mistakes before 3358-streaming-file_with_settings_and_progressmonitor_fixed.diff + the wsgi fix, see this patch, you may have fixed it but i'm not sure. the latest patch I would say is really important for multiple uploads. There are some more upload progress issues, the json encoder gives two value sets but i can't find out why. Posting my latest diff now.

Btw I like one more developer helping me.

08/13/06 19:14:00 changed by toddobryan

OK, you don't have to be very nice. It turns out that I missed perhaps the most important thing, which is wrapping the stream from the POST in some kind of object which prevents lines from being too long and filling up memory.

Oyvind's PostStream? seems like the kind of thing to do, but I'd suggest wrapping the stream before we pass it into the FieldStorage? object, not inside. I was misled by the fact that the read_binary() method inside there has a fixed 8k buffer.

08/13/06 19:30:18 changed by [530]

Toddobryan I agree. Making it more modular is great.

08/13/06 19:36:18 changed by toddobryan

If you could concentrate on getting your progress monitor to work from a file-like object instead of FieldStorage?, I'll integrate the PostStream? idea into my code so that the readline()s in the FieldStorage? class don't read too much.

08/15/06 07:42:59 changed by [530]

Thinking of making the upload monitor as a hook in streaming_upload_middleware.

Then make a upload monitor middleware that hooks onto streaming_upload_middleware by loading before streaming_upload_middleware.

Upload monitor will act exacly like mod_uploadprogress so wsgi users just load streaming_upload_middleware.

08/15/06 07:51:13 changed by mtredinnick

By the way, when you guys think you've addressed all the problems and simplified the code as much as you can, etc, just add a comment pointing to the right patch that needs another review (and mention that you think it's "done").

08/15/06 12:33:14 changed by [530]

To test 'svn up -r 3518', apply patch and add:

django.middleware.upload.UploadStateMiddleware? and django.middleware.upload.StreamingUploadMiddleware? to MIDDLEWARE_CLASSES.

Uploadstate test currently builtin to admin change form. 'tail /tmp/test123' for upload state as json.

08/16/06 11:51:06 changed by anonymous

Regular uploads seem to fail if the middlewares are disabled. Backtrace:

Traceback (most recent call last):
  File "/opt/local/lib/python2.4/site-packages/django/core/servers/basehttp.py", line 272, in run
    self.result = application(self.environ, self.start_response)
  File "/opt/local/lib/python2.4/site-packages/django/core/servers/basehttp.py", line 611, in __call__
    return self.application(environ, start_response)
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 158, in __call__
    response = self.get_response(request.path, request)
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/base.py", line 102, in get_response
    return self.get_technical_error_response(request)
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/base.py", line 134, in get_technical_error_response
    return debug.technical_500_response(request, *sys.exc_info())
  File "/opt/local/lib/python2.4/site-packages/django/views/debug.py", line 131, in technical_500_response
    return HttpResponseServerError(t.render(c), mimetype='text/html')
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 155, in render
    return self.nodelist.render(context)
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 688, in render
    bits.append(self.render_node(node, context))
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 716, in render_node
    raise wrapped
TemplateSyntaxError: Caught an exception while rendering: 'NoneType' object has no attribute 'items'

Original Traceback (most recent call last):
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 706, in render_node
    result = node.render(context)
  File "/opt/local/lib/python2.4/site-packages/django/template/defaulttags.py", line 189, in render
    value = bool_expr.resolve(context, True)
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 548, in resolve
    obj = resolve_variable(self.var, context)
  File "/opt/local/lib/python2.4/site-packages/django/template/__init__.py", line 657, in resolve_variable
    raise VariableDoesNotExist, "Failed lookup for key [%s] in %r" % (bits[0], current) # missing attribute
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 61, in __repr__
    return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 97, in _get_post
    self._load_post_and_files()
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 75, in _load_post_and_files
    self._post, self._files = http.parse_file_upload(self)
  File "/opt/local/lib/python2.4/site-packages/django/http/__init__.py", line 83, in parse_file_upload
    return default_parse_file_upload(req)
  File "/opt/local/lib/python2.4/site-packages/django/http/__init__.py", line 50, in default_parse_file_upload
    raw_message = '\r\n'.join(['%s:%s' % pair for pair in req.header_dict.items()])
AttributeError: 'NoneType' object has no attribute 'items'

08/17/06 06:42:58 changed by Djordjevic Nebojsa <nesh at studioquattro co yu>

  • cc changed from Maniac@SoftwareManiacs.Org to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu.

08/17/06 10:04:14 changed by dp_wiz

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com.

08/18/06 20:49:54 changed by bahamut@macstorm.org

This is anecdotal evidence at best, but  rapid testing of this patch seems to indicate it does not work with the development server.

I basically applied it, updated to head, added the 2 middlewares, tried one of my upload forms, and it just sat there forever, never reaching the Django view code.

08/18/06 21:23:27 changed by [530]

I would not expect this patch to work with the dev server, since it is a single thread you cannot do multiple requests at the same time.

08/18/06 21:45:50 changed by anonymous

I may be mistaken about my last comment, but the dev server uses the wsgi handler. So it may be the delay problem, or that stdin is not passed before the request is done. So since wsgi using lighttpd works, the dev server probably handle things differently. Don't really know alot about the dev server.

08/21/06 18:59:27 changed by anonymous

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com.

08/21/06 19:45:48 changed by hruske

This doesn't work for me at all. With Opera 9 and Firefox 1.5, I get KeyError? on X-Progress-Id, which is supposed to be HTTP_X_PROGRESS_ID? (Webserver is Lighttpd, distro Debian unstable)

Also, isn't this line a bit insecure, given the get_temp_file does an "os.path.join":

content = open(get_temp_file(request.header_dict['X-Progress-Id']), 'r').read()

I've replaced my code to use cache framework instead. But mind you need server-wide cache, per process will probably not be enough.

And then, you probably don't want to open a new TCP connection every time you make a xml request, since this will effectively DOS your server.

I was also having problems with JS - the browser did not want to do a POST at all. It started polling server for progress, but it was not posting at all.

08/22/06 01:46:31 changed by [530]

Lighttpd has a delay before it saves data and progress info.

Supposed to be HTTP_X_PROGRESS_ID?

I did it this way to be compatible with mod_uploadprogress that you should use instead.

Is there another way than opening another tcp connection using xmlhttp?

08/22/06 03:53:24 changed by hruske

How much should that delay usually be? Since there is no POST for Django, progress bar does not work. This probably means I need to use mod_uploadprogress to get it working, but the new mod_uploadprogress has not been released yet officially.

Yes, HTTP_X_PROGRESS_ID. When I printed contents of request.header_dict I could see a key named HTTP_X_PROGRESS_ID which corresponds to X-Progress-Id.

Sure, one could open a TCP connection and server periodically sends info and client reads from it. But I see that you aim for mod_uploadprogress compatibility.

08/22/06 04:40:29 changed by hruske

Also, it's X-Progress-ID in lighttpd, with capital ID.

08/26/06 21:43:49 changed by [530]

  • attachment 3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id.diff added.

Now using X_PROGRESS_ID instead of X-Progress-Id, accepts any lower/uppercase/ - / _ /prefix variant

08/27/06 11:35:39 changed by bahamut@macstorm.org

  • keywords changed from timezone, time zone, UTC to streaming, upload, large.

The patch does not work with Apache 2 + mod_fcgid.

I'm running on Mac OS X 10.4.7 with a pure MacPorts? installation (save Django and flup -- both are a svn HEAD).

Apache 2 configuration:

{{{<IfModule? mod_fcgid.c>

AddHandler? fcgid-script .fcgi

IdleTimeout? 600 ProcessLifeTime? 3600 MaxProcessCount? 8 DefaultMinClassProcessCount? 3 DefaultMaxClassProcessCount? 3 IPCConnectTimeout 8 IPCCommTimeout 48

</IfModule>

ScriptAlias? /myapp "cgi-bin/myapp.fcgi/"}}}

myapp.fcgi:

{{{#!/opt/local/bin/python # -*- coding: utf-8 -*- import sys, os

# Add a custom Python path. sys.path.insert(0, "/opt/local/www")

# Switch to the directory of your project. (Optional.) os.chdir("/opt/local/www/myapp")

# Set the DJANGO_SETTINGS_MODULE environment variable. os.environDJANGO_SETTINGS_MODULE? = "myapp.settings"

from django.core.servers.fastcgi import runfastcgi runfastcgi(["method=threaded", "daemonize=false"])}}}

08/27/06 11:36:56 changed by anonymous

Repost with proper formatting :/

Apache 2 configuration:

<IfModule mod_fcgid.c>
    AddHandler fcgid-script .fcgi
    
    IdleTimeout 600
    ProcessLifeTime 3600
    MaxProcessCount 8
    DefaultMinClassProcessCount 3
    DefaultMaxClassProcessCount 3
    IPCConnectTimeout 8
    IPCCommTimeout 48
</IfModule>

ScriptAlias /myapp "cgi-bin/myapp.fcgi/"

myapp.fcgi:

#!/opt/local/bin/python
# -*- coding: utf-8 -*-
import sys, os

# Add a custom Python path.
sys.path.insert(0, "/opt/local/www")

# Switch to the directory of your project. (Optional.)
os.chdir("/opt/local/www/myapp")

# Set the DJANGO_SETTINGS_MODULE environment variable.
os.environ['DJANGO_SETTINGS_MODULE'] = "myapp.settings"

from django.core.servers.fastcgi import runfastcgi
runfastcgi(["method=threaded", "daemonize=false"])

08/27/06 11:38:29 changed by bahamut@macstorm.org

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org.

09/12/06 08:15:15 changed by bahamut@macstorm.org

I get the following error, again with my mod_fcgid setup, when using the admin interface with the progress status monitor js.

Unhandled exception in thread started by <bound method ThreadPool._worker of <flup.server.threadpool.ThreadPool object at 0x715270>>Traceback (most recent call last):  File "/opt/local/www/data/flup/server/threadpool.py", line 103, in _worker    job.run()  File "/opt/local/www/data/flup/server/fcgi_base.py", line 642, in run    self.process_input()  File "/opt/local/www/data/flup/server/fcgi_base.py", line 678, in process_input    self._do_params(rec)  File "/opt/local/www/data/flup/server/fcgi_base.py", line 777, in _do_params    self._start_request(req)  File "/opt/local/www/data/flup/server/fcgi_base.py", line 761, in _start_request    req.run()  File "/opt/local/www/data/flup/server/fcgi_base.py", line 563, in run    self.server.error(self)  File "/opt/local/www/data/flup/server/fcgi_base.py", line 1157, in error    req.stdout.write('Content-Type: text/html\r\n\r\n' +  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/cgitb.py", line 158, in html    dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/pydoc.py", line 360, in repr    return Repr.repr(self, object)  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/repr.py", line 24, in repr    return self.repr1(x, self.maxlevel)  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/pydoc.py", line 367, in repr1    return self.escape(cram(stripid(repr(x)), self.maxother))  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 65, in __repr__    return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 101, in _get_post    self._load_post_and_files()  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 79, in _load_post_and_files    self._post, self._files = http.parse_file_upload(self)  File "/opt/local/lib/python2.4/site-packages/django/http/__init__.py", line 82, in parse_file_upload    return req.parse_file_upload(req)  File "/opt/local/lib/python2.4/site-packages/django/middleware/upload.py", line 83, in parse_streaming_file_upload    upload_state = req.upload_state(req)  File "/opt/local/lib/python2.4/site-packages/django/middleware/upload.py", line 126, in __init__    self.state = {'size': int(req.header_dict.get('content-length')),TypeError: int() argument must be a string or a number

09/12/06 08:18:51 changed by bahamut@macstorm.org

Something ate line breaks, apparently.

Unhandled exception in thread started by <bound method ThreadPool._worker of <flup.server.threadpool.ThreadPool object at 0x715270>>
Traceback (most recent call last):
  File "/opt/local/www/data/flup/server/threadpool.py", line 103, in _worker
    job.run()
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 642, in run
    self.process_input()
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 678, in process_input
    self._do_params(rec)
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 777, in _do_params
    self._start_request(req)
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 761, in _start_request
    req.run()
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 563, in run
    self.server.error(self)
  File "/opt/local/www/data/flup/server/fcgi_base.py", line 1157, in error
    req.stdout.write('Content-Type: text/html\r\n\r\n' +
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/cgitb.py", line 158, in html
    dump.append('%s&nbsp;= %s' % (name, pydoc.html.repr(value)))
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/pydoc.py", line 360, in repr
    return Repr.repr(self, object)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/repr.py", line 24, in repr
    return self.repr1(x, self.maxlevel)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/pydoc.py", line 367, in repr1
    return self.escape(cram(stripid(repr(x)), self.maxother))
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 65, in __repr__
    return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 101, in _get_post
    self._load_post_and_files()
  File "/opt/local/lib/python2.4/site-packages/django/core/handlers/wsgi.py", line 79, in _load_post_and_files
    self._post, self._files = http.parse_file_upload(self)
  File "/opt/local/lib/python2.4/site-packages/django/http/__init__.py", line 82, in parse_file_upload
    return req.parse_file_upload(req)
  File "/opt/local/lib/python2.4/site-packages/django/middleware/upload.py", line 83, in parse_streaming_file_upload
    upload_state = req.upload_state(req)
  File "/opt/local/lib/python2.4/site-packages/django/middleware/upload.py", line 126, in __init__
    self.state = {'size': int(req.header_dict.get('content-length')),
TypeError: int() argument must be a string or a number

09/12/06 09:32:31 changed by bahamut@macstorm.org

I have just tested the patch with Apache 2 and mod_python. It works correctly, however I had to make 2 small modifications because my Django application isn't running at the root context of my server.

Basically, the admin JavaScript? code hardcodes a request to "/progress/". This needs to be changed appropriately for each install, e.g. in my case "/django/progress/".

Similarly, the UploadStateMiddleware? checks that the request path is exactly "/progress/", which needs to be changed to match.

09/14/06 12:15:32 changed by anonymous

I svn up'ed, applied 3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id.diff, deleted the Django directory under site-packages, reinstalled Django with 'python setup.py install' and restarted apache/modpython but Django appears to be behaving as before. My memory usage still climbs beyond what I'd expect and I don't see any files in /tmp like I'd expect from tempfile.NamedTemporaryFile?().

I added these lines to my settings.py file:

STREAMING_UPLOADS=True

UPLOAD_BUFFER_SIZE=2048

Am I missing something?

Thanks!

09/14/06 14:03:25 changed by oyvind@saltvik.no

It is 2 middleware now.

django.middleware.upload.UploadStateMiddleware and django.middleware.upload.StreamingUploadMiddleware

Order is important

09/14/06 15:44:09 changed by anonymous

Thanks for the info!

I changed middleware_classes to look like this:

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
    'django.middleware.upload.UploadStateMiddleware',
    'django.middleware.upload.StreamingUploadMiddleware',
)

I was able to upload a couple large test files and tempfiles were showing up as expected in /tmp, but I'm intermittently receiving this exception when uploading:

Traceback (most recent call last):
File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/base.py" in get_response
  74. response = callback(request, *callback_args, **callback_kwargs)
File "/home/misjxm/dp1/file/app/views.py" in createmessage
  275. if request.POST:
File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/modpython.py" in _get_post
  51. self._load_post_and_files()
File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/modpython.py" in _load_post_and_files
  32. self._post, self._files = http.parse_file_upload(self)
File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/http/__init__.py" in parse_file_upload
  81. return req.parse_file_upload(req)
File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/middleware/upload.py" in parse_streaming_file_upload
  104. FILES.appendlist(key, FileDict({
File "/usr/lib/python2.4/cgi.py" in __getattr__
  540. raise AttributeError, name

  AttributeError at /file/compose/
  tmp_name

Have you seen this before?

Thanks!

09/14/06 15:52:36 changed by anonymous

And I only seem to get that exception when uploading text files such as M3U playlists, bashrc files, etc.

09/14/06 16:05:45 changed by anonymous

Text files in general only cause the problem intermittently. I have one M3U file that consistantly causes the exception to be thrown. This is what local vars looks like when I upload that file:

name      'tmp_name'
self      FieldStorage('file1', 'klipschtest.m3u', "MP3\\2pac\\Greatest Hits (Disc 1)\\1-05 Hail Mary.mp3\r\nFLAC\\James Blunt\\Back to Bedlam\\03-Wisemen.flac\r\nMP3\\Jimi_Hendrix\\UNKNOWN\\Little wing (extended instrumental).mp3\r\nFLAC\\Led Zeppelin\\Remasters (disc II)\\04-No Quarter.flac\r\nFLAC\\Pink Floyd\\The Wall - Disc 1\\03-Another Brick in the Wall. Part 1.flac\r\nFLAC\\Pink Floyd\\The Wall - Disc 1\\04-The Happiest Days of Our Lives.flac\r\nFLAC\\Natalie Merchant\\Live In Concert\\05-Carnival.flac\r\nMP3\\Metallica\\...And Justice For All\\08-To Live Is To Die.mp3\r\nMP3\\Red Hot Chili Peppers\\Blood Sugar Sex Magik\\10-Blood Sugar Sex Magik.mp3\r\nFLAC\\Sade\\Lovers Rock\\03-King Of Sorrow.flac\r\nFLAC\\Stone, Joss\\The Soul Sessions\\05-Dirty Man.flac\r\nFLAC\\U.G.K\\Ridin' Dirty\\03-Murder.flac\r\nMP3\\Weezer\\Weezer (Green Album)\\04-Weezer _ Island In The Sun.mp3\r\nMP3\\Weezer\\Weezer (Green Album)\\03-Weezer _ Hash Pipe.mp3\r\n")

09/15/06 17:22:33 changed by anonymous

I'm getting an error about 'read only accepts 1 arguement (2 given)", from the following line in cgi.py:

self.fp.read(self.length)

09/15/06 20:57:43 changed by anonymous

This is the error I'm getting:

C:\web\fxedit>python manage.py runserver Validating models... 0 errors found.

Django version 0.95, using settings 'fxedit.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK. [16/Sep/2006 02:55:31] "GET /file/ HTTP/1.1" 200 1188 [16/Sep/2006 02:55:31] "GET /media/stylesheets/style.css HTTP/1.1" 304 0 [16/Sep/2006 02:55:31] "GET /media/stylesheets/print.css HTTP/1.1" 304 0 Traceback (most recent call last):

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\servers\

basehttp.py", line 272, in run

self.result = application(self.environ, self.start_response)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\servers\

basehttp.py", line 615, in call

return self.application(environ, start_response)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\wsgi.py", line 156, in call

response = self.get_response(request.path, request)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\base.py", line 102, in get_response

return self.get_technical_error_response(request)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\base.py", line 134, in get_technical_error_response

return debug.technical_500_response(request, *sys.exc_info())

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\views\debug.p

y", line 131, in technical_500_response

return HttpResponseServerError?(t.render(c), mimetype='text/html')

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 155, in render

return self.nodelist.render(context)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 688, in render

bits.append(self.render_node(node, context))

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 716, in render_node

raise wrapped

TemplateSyntaxError?: Caught an exception while rendering: read() takes exactly 1

argument (2 given)

Original Traceback (most recent call last):

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 706, in render_node

result = node.render(context)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\defa

ulttags.py", line 189, in render

value = bool_expr.resolve(context, True)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 548, in resolve

obj = resolve_variable(self.var, context)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\template\in

it.py", line 657, in resolve_variable

raise VariableDoesNotExist?, "Failed lookup for key [%s] in %r" % (bits[0], c

urrent) # missing attribute

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\wsgi.py", line 61, in repr

return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\wsgi.py", line 97, in _get_post

self._load_post_and_files()

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\core\handlers

\wsgi.py", line 75, in _load_post_and_files

self._post, self._files = http.parse_file_upload(self)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\http\init

.py", line 80, in parse_file_upload

return req.parse_file_upload(req)

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\middleware\up

load.py", line 87, in parse_streaming_file_upload

fs = FieldStorage?(req.raw_request, environ=req.META, headers=req.header_dict

, upload_state=upload_state )

File "C:\Python24\lib\site-packages\django-0.95-py2.4.egg\django\middleware\up

load.py", line 71, in init

keep_blank_values=keep_blank_values, strict_parsing=strict_parsing)

File "C:\Python24\lib\cgi.py", line 524, in init

self.read_urlencoded()

File "C:\Python24\lib\cgi.py", line 629, in read_urlencoded

qs = self.fp.read(self.length)

TypeError?: read() takes exactly 1 argument (2 given)

[16/Sep/2006 02:55:53] "POST /file/stats/ HTTP/1.1" 500 3721

09/20/06 08:58:51 changed by [Cha0S]

  • attachment 3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id_windowsfix.diff added.

windows temporary file locking fix

09/20/06 19:32:34 changed by SmileyChris

I'm getting the same error as anonymous above (and am also using Windows).

10/07/06 23:14:44 changed by oyvind@saltvik.no

Idea for fixing the m3u upload problem. May need a copy of this util.py to make it work with wsgi.

But I have almost given up on wsgi for this patch, uless anyone are able to shed som light on why wsgi does not like chunked reading from wsgi.input.

http://svn.apache.org/viewvc/httpd/mod_python/trunk/lib/python/mod_python/util.py?view=markup&pathrev=451861

11/08/06 13:01:42 changed by [Cha0S]

There is a problem using this patch with Nginx (and maybe some other webservers). Nginx doesn't pass content-type and content-length headers and the result of this is that FieldStorage? can't parse request correctly and nothing is uploaded. A workaround I've found is modification of cgi.FieldStorage? to use HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH. But I don't think this is elegant solution of this problem. Does anyone have other suggestions on how to solve it ?

11/11/06 21:13:53 changed by Sung-Jin Hong <serialx.net@gmail.com>

Worked for me(Apache mod_python). Working like charm. :) But heres some instructions..

  1. Do the patch (cd django; patch -Np0 < file.diff)
  2. Add two middlewares, modify the model like below:
    class FileList(models.Model):
        name = models.CharField(maxlength=255)
        email = models.EmailField()
    
        class Admin:
            js = ['js/UploadProgress.js']
    
    class AFile(models.Model):
        descr = models.CharField(maxlength=255,core=True)
        file = models.FileField(upload_to='files')
        inlist = models.ForeignKey(FileList,edit_inline=models.STACKED)
    
  1. You have progressbar in admin. :)

Submitting patch to the latest trunk.

11/11/06 21:17:16 changed by Sung-Jin Hong <serialx.net@gmail.com>

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com.

11/13/06 08:28:22 changed by [Cha0S]

There are some issues with this patch. I have it working on my local Linux and Windows boxes. But i can't get to work it completely correct on my server with the same setup as my local linux box (python 2.5, latest Django). The issue is that when uploading a file WSGIRequest . _load_post_and_files is called only after all data is uploaded. So no streaming occurs and no progress is displayed and I didn't find a solution for this yet..

11/17/06 17:56:44 changed by Øyvind Saltvik <oyvind.saltvik@gmail.com>

[Cha0S] using lighttpd or apache, same lighttpd/apache version on both servers? I found out lighttpd pre 1.4.0 supported streaming, so that has to be fixed before this patch will work on lighttpd.

11/18/06 20:15:14 changed by anonymous

This worked great on my system [Apache/2.0.58 (Win32) mod_python/3.2.8 Python/2.4.3]

Following up on Sung-Jin Hong's good instructions with a few tips for newbies like me:

1. When you apply the patch, make sure you get django/middleware/upload.py. I found that when I did an SVN update to Rev 4065 and applied 4065-streaming_uploads_and_uploadprogress_middleware_x_progress_id_windowsfix.diff I did not get the file. I had to apply 3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id_windowsfix.diff to get it.

2. Make sure js/UploadProgress.js in accessible. The Admin page's path to it will be in the media directory, e.g. http://yoursite.com/media/js/UploadProgress.js. I had to copy UploadProgress?.js from django/contrib/admin/media/js to my local media/js directory.

3. Make sure the Apache httpd.conf file section for your site allows paths starting with "/progress", i.e. has a line like '<Location ~ "/(admin|shop|progress)">' in it, because the JS code is doing a "req.open("GET", "/progress/", 1)" repeatedly to track progress.

I find that the Upload progress box displays fine in Firefox and Opera, but it is at the very bottom left of the absolute page in IE6 (i.e. you need to scroll to the very bottom to see it). I'm still working on this problem.

Thank you to all who contributed this!

11/24/06 09:46:00 changed by [Cha0S]

[Cha0S] using lighttpd or apache, same lighttpd/apache version on both servers? I found out lighttpd pre 1.4.0 supported streaming, so that has to be fixed before this patch will work on lighttpd.

I've tested with lighttpd and nginx web-servers. same versions on both servers. with apache it works fine on windows.

12/08/06 23:41:38 changed by jacobm3 A T gmail.com

  • type changed from enhancement to defect.
  • component changed from Core framework to Contrib apps.

Has anyone come up with a fix for the AttributeError? exceptions when uploading text files? I'm still getting exceptions like this:

AttributeError? at /file/compose/ tmp_name Request Method: POST Request URL: https://myserver/file/compose/ Exception Type: AttributeError? Exception Value: tmp_name Exception Location: /usr/lib/python2.4/cgi.py in getattr, line 540

Traceback (most recent call last): File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/base.py" in get_response

  1. response = callback(request, *callback_args, **callback_kwargs)

File "/files/lib/file/app/views.py" in createmessage

  1. if request.POST:

File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/modpython.py" in _get_post

  1. self._load_post_and_files()

File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/core/handlers/modpython.py" in _load_post_and_files

  1. self._post, self._files = http.parse_file_upload(self)

File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/http/init.py" in parse_file_upload

  1. return req.parse_file_upload(req)

File "/usr/lib/python2.4/site-packages/Django-0.95-py2.4.egg/django/middleware/upload.py" in parse_streaming_file_upload

  1. FILES.appendlist(key, FileDict?({

File "/usr/lib/python2.4/cgi.py" in getattr

  1. raise AttributeError?, name

AttributeError? at /file/compose/ tmp_name

12/08/06 23:42:37 changed by anonymous

  • type changed from defect to enhancement.

oops, didn't mean to change the type on this ticket... changing back to enhancement.

12/08/06 23:43:07 changed by anonymous

  • component changed from Contrib apps to Core framework.

12/13/06 21:54:34 changed by wonlay@gmail.com

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com, wonlay@gmail.com.

Hi, jacob The 'AttributeError?' occurred, because FieldStorge? does not make_temp_file for small files, and the tmp_name is not defined. The progress middleware has problem too. You will notice that your upload speed will down to a significant lower rate, if you use this middleware. The performance bottleneck is that, every single 'read' from request reads only a few bytes, even if you set a large buffer_size, and the serialization and save to disc of 'UploadState?' is significant.

The attachments is my working copy of code.

01/15/07 19:37:25 changed by anonymous

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com, wonlay@gmail.com to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com, wonlay@gmail.com, lurker86@gmail.com.

01/25/07 04:16:39 changed by Simon G. <dev@simon.net.nz>

  • needs_better_patch set to 1.
  • stage changed from Unreviewed to Accepted.
  • needs_docs set to 1.

Can someone who has used this give us a progress report on this - which of the 3000 patches above are needed? can these be merged into one patch that works?

01/26/07 07:31:06 changed by anonymous

The one that i can vouch for is 3581-streaming_uploads_and_uploadprogress_middleware_x_progress_id.diff

But it has problems with m3u uploads's for some reason i can't figure out.

Was thinking about the possibility of using email.FeedParser instead of rfc822 but that is not available in python 2.3

This patch does not work with lighttpd, since lighttpd can't stream requests.

01/26/07 08:34:38 changed by Ivan Sagalaev <Maniac@SoftwareManiacs.Org>

This patch does not work with lighttpd, since lighttpd can't stream requests.

This is not true. When Django works with Lighttpd over FastCGI lighttpd immediately offloads received data to Django process and it can stream it wherever it wants to. In fact I use one of my services that uploads zipped mp3 albums under lighttpd and streaming works (with my patch in #1484).

01/26/07 11:04:39 changed by Øyvind Saltvik <oyvind.saltvik@gmail.com>

I think you are correct. I guess the problem with this patch and fcgi has to do with not doing blocking reads with a buffer size. But a buffer size is needed if the browser sends a big file without eol's, and for the upload progress.

01/26/07 11:37:28 changed by Ivan Sagalaev <Maniac@SoftwareManiacs.Org>

I'm not sure I understand what you're saying... I was replying solely to a claim that lighttpd can't do streaming. It certainly does streaming to a FCGI app. Whatever the app does then (streaming by blocks, by eols or no streaming at all) is a whole another question.

01/30/07 10:03:50 changed by shaunc <shaun@cuttshome.net>

  • cc changed from Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com, wonlay@gmail.com, lurker86@gmail.com to Maniac@SoftwareManiacs.Org, nesh@studioquattro.co.yu, aenor.realm@gmail.com, gary.wilson@gmail.com, bahamut@macstorm.org, serialx.net@gmail.com, wonlay@gmail.com, lurker86@gmail.com, shaun@cranedata.us.

01/31/07 13:24:24 changed by Øyvind Saltvik <oyvind.saltvik@gmail.com>

02/01/07 07:12:03 changed by Øyvind Saltvik <oyvind@saltvik.no>

Need to override