[mod_python] req.sendfile() questions

Jim Gallacher jg.lists at sympatico.ca
Mon Jun 20 11:43:34 EDT 2005


Wouter van Marle wrote:
> On Mon, 2005-06-20 at 08:37 -0400, Jim Gallacher wrote:
> 
> 
>>>I'm looking into a way of sending out specific files, and detect whether
>>>or not the download was successful.
>>>For sending the file I think I can best use req.sendfile(path,[offset,
>>>len]). That seems easy enough to implement.
>>>
>>>But how to detect whether the download was aborted (if so: at what
>>>stage), or whether it has finished successful, etc. What I understand
>>>after searching the web, is that req.sendfile does come back: just no
>>>documentation on what it comes back with!
>>
>>It will raise an IOError exception if the client disconnects. Otherwise 
>>it will return the number of bytes sent.
>>
> 
> 
> OK, I understand.
> So basically I should say something like this (totally untested for
> now):
> 
> def send_file(req):
> 	filename = "/path/to/file.zip"
> 	try:
> 		bytes_sent = req.sendfile(filename)
> 		success = True
> 	except IOError:
> 		# client disconnected!
> 		success = False

Seems reasonable.

> I've already noticed you have to add a line like:
> req.headers_out['Content-Disposition'] = 'attachment; filename=file.zip'
> to give the browser a file name for saving (and in case of Mozilla at
> least forces the saving of an mp3 file instead of playing immediately),
> and:
> req.content_type = mimetype
> to set the correct mime type. 
> 
> Another, related problem that I ran into: how to write some data to the
> browser window, and then send the file? That didn't work. Or after the
> download is finished, to write some data to the browser. It seems I have
> to start a new request or so? Changing the content_type does not help!

This is a limitation of HTTP. The content type is sent with the response 
header. Once you start sending content, it's too late to change anything 
in the header. Basically, you get one content_type per request.

You could try either req.internal_redirect() and util.redirect() but I'm 
not hopeful. I suspect they'll both complain about the headers already 
being sent.

> 
>>req.sendfile is a wrapper around the apache function ap_send_fd(). A 
>>quick look at this tells me that nbytes is set to 0 on failure, so there 
>>is no way for mod_python to determine the progress if the client 
>>disconnects.
>>
> 
> 
> That's less important to me; complete or not is the main issue here.
> By the way, does the req.sendfile() function support resuming a
> download?

Ah, so it *is* important to you. :) ap_send_fd() sets the number of 
bytes sent to 0 on failure - ie the client disconnects. There is no way 
for req.sendfile to know the number of bytes sent, so there is no way 
for it to resume a download, since it would not know where in the file 
to start sending.

You may be able to manipulate the http headers to get what you want. 
Take a look at headers such as Accept-Ranges, Content-Length, Range, 
etc. This will depend on the client also being able to resume downloads 
and sending the appropriate request. You will need to examine the 
request header to determine what part of the file to send in response.

See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Regards,
Jim


More information about the Mod_python mailing list