views:

406

answers:

1

I want to return some files in a HttpResponse and I'm using the following function. The file that is returned always has a filesize of 1kb and I do not know why. I can open the file, but it seems that it is not served correctly. Thus I wanted to know how one can return files with django/python over a HttpResponse.

@login_required
def serve_upload_files(request, file_url):
    import os.path
    import mimetypes
    mimetypes.init()

    try:
        file_path = settings.UPLOAD_LOCATION + '/' + file_url
        fsock = open(file_path,"r")
        #file = fsock.read()
        #fsock = open(file_path,"r").read()
        file_name = os.path.basename(file_path)
        file_size = os.path.getsize(file_path)
        print "file size is: " + str(file_size)
        mime_type_guess = mimetypes.guess_type(file_name)
        if mime_type_guess is not None:
            response = HttpResponse(fsock, mimetype=mime_type_guess[0])
        response['Content-Disposition'] = 'attachment; filename=' + file_name            
    except IOError:
        response = HttpResponseNotFound()
    return response

Edit: The bug is actually not a bug ;-)

This solution is working in production on an apache server, thus the source is ok.

While writing this question I tested it local with the django development server and was wondering why it does not work. A friend of mine told me that this issue could arise if the mime types are not set in the server. But he was not sure if this is the problem. But one thing for sure.. it has something to do with the server.

+3  A: 

Try passing the fsock iterator as a parameter to HttpResponse(), rather than to its write() method which I think expects a string.

response = HttpResponse(fsock, mimetype=...)

See http://docs.djangoproject.com/en/dev/ref/request-response/#passing-iterators

Also, I'm not sure you want to call close on your file before returning response. Having played around with this in the shell (I've not tried this in an actual Django view), it seems that the response doesn't access the file until the response itself is read. Trying to read a HttpResponse created using a file that is now closed results in a ValueError: I/O operation on closed file.

So, you might want to leave fsock open, and let the garbage collector deal with it after the response is read.

Ben James
Hi, thanks for your answer. Unfortunately it does not change anything. response = HttpResponse(fsock, mimetype=mime_type_guess[0]) . The file is still not served correctly. Additionally I checked the file-size (with file_size = os.path.getsize(file_path) print "file size is: " + str(file_size)) before I return it over the HttpResponse and it returns: file size is: 56349 . Thus the file is read correctly from the file system and something went wrong when it is returned.
Tom Tom
Tom Tom, see additions to my answer.
Ben James
I agree with Ben. When fsock is added to the HttpResponse, either in the constructor or using the `write` method, it gets added to the internal stack of content that HttpResponse maintains. That stack is not evaluated until later, at the last possible moment when the response is actually needed. You'll need to leave the fsock open or else HttpResponse will raise an error when attempted to read from it. The garbage collector will take care of closing the file.
Jarret Hardie
Ok thanks. I left the fsock open and I do not see the error message again. But the file-size is still 1kb :(
Tom Tom
If you have changed the code, can you please update your question to reflect it? There are more things I could mention but it becomes possibly irrelevant if you have significantly changed the code now.
Ben James
Oh yes you are right. I updated it now.
Tom Tom