3

I'm trying to make a script for downloading the uploaded files, on the user's machine. The problem is that the download simply doesn't work (it either downloads me an empty file, or gives me some errors).

the last error is: coercing to Unicode: need string or buffer, FieldFile found

def download_course(request, id):
    course = Courses.objects.get(pk = id).course

    path_to_file = 'root/cFolder'
    filename = course # Select your file here.                                
    wrapper = FileWrapper(file(course))
    content_type = mimetypes.guess_type(filename)[0]
    response = HttpResponse(wrapper, content_type = content_type)
    response['Content-Length'] = os.path.getsize(filename)
    response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(course)

    return response

how can I declare properly the filename so that it will know each time what file to be downloading: the filename is actually 'course' as declared above

Thanks !

Arghya Saha
  • 5,599
  • 4
  • 26
  • 48
dana
  • 5,168
  • 20
  • 75
  • 116

2 Answers2

5

edited

I think that you need to extract path value from FileField object:

def download_course(request, id):
    course = Courses.objects.get(pk = id).course

    path = course.path # Get file path
    wrapper = FileWrapper( open( path, "r" ) )
    content_type = mimetypes.guess_type( path )[0]

    response = HttpResponse(wrapper, content_type = content_type)
    response['Content-Length'] = os.path.getsize( path ) # not FileField instance
    response['Content-Disposition'] = 'attachment; filename=%s/' % \ 
                                       smart_str( os.path.basename( path ) ) # same here

    return response

Why is that:

Let's say I have (well, I actually have) Model:

class DanePracodawcy( DaneAdresowe, DaneKontaktowe ):
    # other fields
    logo = ImageWithThumbnailsField( upload_to = 'upload/logos/',
                                  thumbnail = {'size': (180, 90)},
                                  blank = True )

ImageWithThumbnailsField is subclass of FileField, so it behaves the same way. Now, when I do SELECT:

mysql> select logo from accounts_danepracodawcy;
+-----------------------------+
| logo                        |
+-----------------------------+
| upload/logos/Lighthouse.jpg |
+-----------------------------+
1 row in set (0.00 sec)

it shows (relative to MEDIA_ROOT) path of stored file. But when I access logo Model attribute:

[D:projekty/pracus]|1> from accounts.models import DanePracodawcy
[D:projekty/pracus]|4> DanePracodawcy.objects.get().logo
                   <4> <ImageWithThumbnailsFieldFile: upload/logos/Lighthouse.jpg>
[D:projekty/pracus]|5> type( _ )
                   <5> <class 'sorl.thumbnail.fields.ImageWithThumbnailsFieldFile'>

I get instance of some object. If I try to pass that instance to os.path.getsize:

[D:projekty/pracus]|8> import os.path
[D:projekty/pracus]|9> os.path.getsize( DanePracodawcy.objects.get().logo )
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

D:\projekty\pracus\<ipython console> in <module>()

C:\Python26\lib\genericpath.pyc in getsize(filename)
     47 def getsize(filename):
     48     """Return the size of a file, reported by os.stat()."""
---> 49     return os.stat(filename).st_size
     50
     51

TypeError: coercing to Unicode: need string or buffer, ImageWithThumbnailsFieldFile found

I get TypeError, like you. So I need file path as string, which can be obtained with path attribute:

[D:projekty/pracus]|13> os.path.getsize(  DanePracodawcy.objects.get().logo.path )
                   <13> 561276L

Alternatively, I could get name attribute and os.path.join it with MEDIA_ROOT setting:

[D:projekty/pracus]|11> from django.conf import settings
[D:projekty/pracus]|12> os.path.getsize(  os.path.join( settings.MEDIA_ROOT, DanePracodawcy.objects.get().logo.name ) )
                   <12> 561276L

But that's unnecessary typing.

Last thing to note: because path is absolute path, I need to extract filename to pass it to Content-Disposition header:

[D:projekty/pracus]|16> DanePracodawcy.objects.get().logo.path
                   <16> u'd:\\projekty\\pracus\\site_media\\upload\\logos\\lighthouse.jpg'
[D:projekty/pracus]|17> os.path.basename( DanePracodawcy.objects.get().logo.path )
                   <17> u'lighthouse.jpg'
cji
  • 6,635
  • 2
  • 20
  • 16
  • nop, the name of the course is the correspondant of the 'course' field in the table – dana Jul 09 '10 at 14:43
  • hmm .. that's so weird cause my error is still coercing to Unicode: need string or buffer, FieldFile found . i really don;t understand why. – dana Jul 09 '10 at 15:17
  • 1
    Sorry, my answer was wrong, I checked again and edited. Now code above should work as expected and I tried to explain why it works that way. – cji Jul 09 '10 at 15:38
  • it works perfect !! just you forgotten to declare filename = course.name there. I have accepted the answer. Would you please post a link to your answer here too:http://stackoverflow.com/questions/3141682/django-download-file-empty (i've made a bounty, and i want to accept your answer there too. thnaks!) – dana Jul 09 '10 at 15:48
  • I edited one last time, to correct undeclared filename (I named that variable `path`, as I think it's more apropriate that way in this code). Glad I could help :) – cji Jul 09 '10 at 15:55
2

Unless you are letting the user download a dynamically generated file, I don't see why you need to do all that.

You can just let this view redirect to the appropriate path, and the respective headers are set by the server serving the static files; typically apache or nginx

I'd do your this view as follows:

from django.conf import settings

def download_course(request,id):
    course = get_object_or_404(Course,id=id)
    filename = course.course
    return redirect('%s/%s'%(settings.MEDIA_URL,filename))

Enjoy :)

lprsd
  • 84,407
  • 47
  • 135
  • 168