I am trying to send a base64-encoded excel file through Django post_office lib. Here's the code that I have now
An example of encoded content:
body = 'UEsDBBQAAAAIAAAAPwBhXUk6TwEAAI8EAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbK2Uy27CMBBF9/2KyNsqMXRRVRWBRR/LFqn0A1x7Qiwc2/IMFP6+k/BQW1Gggk2sZO7cc8eOPBgtG5ctIKENvhT9oicy8DoY66eleJ8853ciQ1LeKBc8lGIFKEbDq8FkFQEzbvZYipoo3kuJuoZGYREieK5UITWK+DVNZVR6pqYgb3q9W6mDJ/CUU+shhoNHqNTcUfa05M/rIAkciuxhLWxZpVAxOqsVcV0uvPlFyTeEgjs7DdY24jULhNxLaCt/AzZ9r7wzyRrIxirRi2pYJU3Q4xQiStYXh132xAxVZTWwx7zhlgLaQAZMHtkSElnYZT7I1iHB/+HbPWq7TyQunURaOcCzR8WYQBmsAahxxdr0CJn4f4L1s382v7M5AvwMafYRwuzSw7Zr0SjrT+B3YpTdcv7UP4Ps/I8dea0SmDdKfA1c/OS/e29zyO4+GX4BUEsDBBQAAAAIAAAAPwDyn0na6QAAAEsCAAALAAAAX3JlbHMvLnJlbHOtksFOwzAMQO98ReT7mm5ICKGluyCk3SY0PsAkbhu1jaPEg+7viZBADI1pB45x7Odny+vNPI3qjVL2HAwsqxoUBcvOh87Ay/5pcQ8qCwaHIwcycKQMm+Zm/UwjSqnJvY9ZFUjIBnqR+KB1tj1NmCuOFMpPy2lCKc/U6Yh2wI70qq7vdPrJgOaEqbbOQNq6Jaj9MdI1bG5bb+mR7WGiIGda/MooZEwdiYF51O+chlfmoSpQ0OddVte7/D2nnkjQoaC2nGgRU6lO4stav3Uc210J58+MS0K3/7kcmoWCI3dZCWP8MtInN9B8AFBLAwQUAAAACAAAAD8ARHVb8OgAAAC5AgAAGgAAAHhsL19yZWxzL3dvcmtib29rLnhtbC5yZWxzrZLBasMwEETv/Qqx91p2EkopkXMphVzb9AOEtLZMbElot2n99xEJTR0IoQefxIzYmQe7683P0IsDJuqCV1AVJQj0JtjOtwo+d2+PzyCItbe6Dx4VjEiwqR/W79hrzjPkukgih3hS4Jjji5RkHA6aihDR558mpEFzlqmVUZu9blEuyvJJpmkG1FeZYmsVpK2tQOzGiP/JDk3TGXwN5mtAzzcq5HdIe3KInEN1apEVXCySp6cqcirI2zCLOWE4z+IfyEmezbsMyzkZiMc+L/QCcdb36lez1jud0H5wytc2pZjavzDy6uLqI1BLAwQUAAAACAAAAD8AzgFzhmwBAAALAwAAGAAAAHhsL3dvcmtzaGVldHMvc2hlZXQxLnhtbI2SyW6DMBCG730Ky/fGLEpbISDKoqg9VKq63R0YwAp4kO2E9u1rQxIhkkNvs/7fzNjx4qepyRGUFigT6s88SkBmmAtZJvTrc3v/RIk2XOa8RgkJ/QVNF+ld3KHa6wrAECsgdUIrY9qIMZ1V0HA9wxakzRSoGm6sq0qmWwU875uamgWe98AaLiQdFCL1Hw0sCpHBBrNDA9IMIgpqbuz4uhKtpmmcC5tz+xAFRUKXfrQOKUvjnvwtoNMjmxi++4AaMgO53Z8St9gOce+SLzbkuVZ21bvth3pTJIeCH2rzjt0ziLIyVmR+oW244WmssCOqF9ctd7fyo9DOmbngykX7nO100x9TL2ZHi8xOFevrCv9Swaz2BRDcAiyDU/sN6VUwkRuAwQgV3EaFN1HhCDURXYUTuQEVjlDhBMVGJ2x5Ca9clUJqUkNhe7zZIyVquHhvG2x7a07JDo3B5uxV9teBcp6lFYjm7LiHvfzj9A9QSwMEFAAAAAgAAAA/AIMYaiVIAQAAJgIAAA8AAAB4bC93b3JrYm9vay54bWyNUctOwzAQvPMV1t5pHmojWjWpxEtUQoBEac8m3jRWHTuyHdL+PetUKXDjtDPj3dHOerk6Nop9oXXS6BySSQwMdWmE1PscPjaP1zfAnOdacGU05nBCB6viatkbe/g05sBoXrscau/bRRS5ssaGu4lpUdNLZWzDPVG7j1xrkQtXI/pGRWkcZ1HDpYazw8L+x8NUlSzx3pRdg9qfTSwq7ml7V8vWQbGspMLtORDjbfvCG1r7qIAp7vyDkB5FDlOipsc/gu3a206qQGbxDKLiEvLNMoEV75Tf0GqjO50rnaZpFjpD11Zi736GAmXHndTC9DmkU7rsaWTJDFg/4J0UviYhi+cX7QnlvvY5zLMsDubRL/fhfmNlegj3HnBC/xTqmvYnbBeSgF2LZHAYx0quSkoTytCYTmfJHFjVKXVH2qt+NnwwCENjkuIbUEsDBBQAAAAIAAAAPwBcdNNwogAAAOsAAAAUAAAAeGwvc2hhcmVkU3RyaW5ncy54bWxdzk0KwjAQhuG9pwiz11QREUniQvAEeoDYjm0gmdTM1J/bWxERunyf4YMx+2eK6o6FQyYLy0UFCqnOTaDWwvl0nG9BsXhqfMyEFl7IsHczwyxqnBJb6ET6ndZcd5g8L3KPNF6uuSQvY5ZWc1/QN9whSop6VVUbnXwgUHUeSCysQQ0UbgMefu0MB2fEeaPFGf2JL1ym0GGMeYqPXGLzRz1+695QSwMEFAAAAAgAAAA/AGmuhBj7AQAAPQUAAA0AAAB4bC9zdHlsZXMueG1svVTfi5wwEH7vXxHyfucq9GiLevQKC4W2FG4LfY0aNZAfkoyL3l/fSeKqC3cs3ENfzMzkm29mvsTkj5OS5MytE0YXNL0/UMJ1bRqhu4L+OR3vPlHigOmGSaN5QWfu6GP5IXcwS/7ccw4EGbQraA8wfEkSV/dcMXdvBq5xpzVWMUDXdokbLGeN80lKJtnh8JAoJjQt89ZocKQ2o4aCZkugzN0LOTOJbaU0KfPaSGMJID32ESKaKR4R35gUlRU+2DIl5BzDmQ+EjhacEtpYH0xihfitkv9RKywOk4SU18NioMwHBsCtPqJDFvs0D1heo/CRJuBuoDvL5jT7uEsIC9atjG3woPeVY6jMJW8BE6zoer+CGRK/CWAUGo1gndFMespLxj6ThMtQUOjDYUbt2AhmkS7xoIX9JjagQgs3oYi5dHkTG2Gvz7IYKFHNpXz2TH/bVacU+aaW6FEdFXxvCoq/iD/Ji4niLmakiY7n37NF7h1t9i5aMrUr/1vZ6RvZ6ZZN2DDI+WjifNF7CsDN/ypFpxW/SMAuLumNFS+Y6u94jQFuqX9BQNQ+gocShp/aRYF1+CDFlaxrlPi/q6C//GMhd21Wo5Ag9CuSImczbWqGXWAVvklXVZCj4S0bJZzWzYJu9k/eiFF9XlG/xdnAgtrsH/5Opg+hg+3hK/8BUEsDBBQAAAAIAAAAPwAY+kZUsAUAAFIbAAATAAAAeGwvdGhlbWUvdGhlbWUxLnhtbO1ZTY/bRBi+8ytGvreOEzvNrpqtNtmkhe22q920qMeJPbGnGXusmcluc0PtEQkJURAXJG4cEFCplbiUX7NQBEXqX+D1R5LxZrLNtosAtTkknvHzfn/4HefqtQcxQ0dESMqTtuVcrlmIJD4PaBK2rTuD/qWWhaTCSYAZT0jbmhJpXdv64CreVBGJCQLyRG7ithUplW7atvRhG8vLPCUJ3BtxEWMFSxHagcDHwDZmdr1Wa9oxpomFEhwD19ujEfUJGmQsra0Z8x6Dr0TJbMNn4tDPJeoUOTYYO9mPnMouE+gIs7YFcgJ+PCAPlIUYlgputK1a/rHsrav2nIipFbQaXT//lHQlQTCu53QiHM4Jnb67cWVnzr9e8F/G9Xq9bs+Z88sB2PfBUmcJ6/ZbTmfGUwMVl8u8uzWv5lbxGv/GEn6j0+l4GxV8Y4F3l/CtWtPdrlfw7gLvLevf2e52mxW8t8A3l/D9KxtNt4rPQRGjyXgJncVzHpk5ZMTZDSO8BfDWLAEWKFvLroI+UatyLcb3uegDIA8uVjRBapqSEfYB18XxUFCcCcCbBGt3ii1fLm1lspD0BU1V2/ooxVARC8ir5z+8ev4UvXr+5OThs5OHP588enTy8CcD4Q2chDrhy+8+/+ubT9CfT799+fhLM17q+N9+/PTXX74wA5UOfPHVk9+fPXnx9Wd/fP/YAN8WeKjDBzQmEt0ix+iAx2CbQQAZivNRDCJMKxQ4AqQB2FNRBXhripkJ1yFV590V0ABMwOuT+xVdDyMxUdQA3I3iCnCPc9bhwmjObiZLN2eShGbhYqLjDjA+Msnungptb5JCJlMTy25EKmruM4g2DklCFMru8TEhBrJ7lFb8ukd9wSUfKXSPog6mRpcM6FCZiW7QGOIyNSkIoa74Zu8u6nBmYr9DjqpIKAjMTCwJq7jxOp4oHBs1xjHTkTexikxKHk6FX3G4VBDpkDCOegGR0kRzW0wr6u5i6ETGsO+xaVxFCkXHJuRNzLmO3OHjboTj1KgzTSId+6EcQ4pitM+VUQlerZBsDXHAycpw36VEna+s79AwMidIdmciyq5d6b8xTc5qxoxCN37fjGfwbXg0mUridAtehfsfNt4dPEn2CeT6+777vu++i313VS2v220XDdbW5+KcX7xySB5Rxg7VlJGbMm/NEpQO+rCZL3Ki+UyeRnBZiqvgQoHzayS4+piq6DDCKYhxcgmhLFmHEqVcwknAWsk7P05SMD7f82ZnQEBjtceDYruhnw3nbPJVKHVBjYzBusIaV95OmFMA15TmeGZp3pnSbM2bUA0IZwd/p1kvREPGYEaCzO8Fg1lYLjxEMsIBKWPkGA1xGmu6rfV6r2nSNhpvJ22dIOni3BXivAuIUm0pSvZyObKkukLHoJVX9yzk47RtjWCSgss4BX4ya0CYhUnb8lVpymuL+bTB5rR0aisNrohIhVQ7WEYFVX5r9uokWehf99zMDxdjgKEbradFo+X8i1rYp0NLRiPiqxU7i2V5j08UEYdRcIyGbCIOMOjtFtkVUAnPjPpsIaBC3TLxqpVfVsHpVzRldWCWRrjsSS0t9gU8v57rkK809ewVur+hKY0LNMV7d03JMhfG1kaQH6hgDBAYZTnatrhQEYculEbU7wsYHHJZoBeCsshUQix735zpSo4WfavgUTS5MFIHNESCQqdTkSBkX5V2voaZU9efrzNGZZ+ZqyvT4ndIjggbZNXbzOy3UDTrJqUjctzpoNmm6hqG/f/w5OOumHzOHg8WgtzzzCKu1vS1R8HG26lwzkdt3Wxx3Vv7UZvC4QNlX9C4qfDZYr4d8AOIPppPlAgS8VKrLL/55hB0bmnGZaz+2TFqEYLWinhf5PCpObuxwtlni3tzZ3sGX3tnu9peLlFbO8jkq6U/nvjwPsjegYPShClZvE16AEfN7uwvA+BjL0i3/gZQSwMEFAAAAAgAAAA/ANOT8nMkAQAAUAIAABEAAABkb2NQcm9wcy9jb3JlLnhtbJ2SzWrDMBCE730Ko7stKYZihO1AW3JqoNCUlt6EtElErR8ktU7evoqTOAn4lONqZr+dXVTPd7rL/sAHZU2DaEFQBkZYqcymQR+rRV6hLERuJO+sgQbtIaB5+1ALx4T18OatAx8VhCyBTGDCNWgbo2MYB7EFzUORHCaJa+s1j6n0G+y4+OEbwDNCHrGGyCWPHB+AuRuJ6ISUYkS6X98NACkwdKDBxIBpQfHFG8HrMNkwKFdOreLewaT1LI7uXVCjse/7oi8Ha8pP8dfy9X1YNVfmcCoBqK2lYMIDj9a3Nb4u0uE6HuIynXitQD7tkz7xdlrk2AcySwHYMe5Z+SyfX1YL1M7IjOakymm5opQRwmj1fRh5038B6tOQu4lnwDH37Sdo/wFQSwMEFAAAAAgAAAA/AF66p9N3AQAAEAMAABAAAABkb2NQcm9wcy9hcHAueG1snZLBTuswEEX3fEXkPXVSIfRUOUaogFjwRKUWWBtn0lg4tuUZopavx0nVkAIrsrozc3V9Mra42rU26yCi8a5kxSxnGTjtK+O2JXva3J3/YxmScpWy3kHJ9oDsSp6JVfQBIhnALCU4LFlDFBaco26gVThLY5cmtY+tolTGLfd1bTTceP3egiM+z/NLDjsCV0F1HsZAdkhcdPTX0Mrrng+fN/uQ8qS4DsEarSj9pPxvdPToa8pudxqs4NOhSEFr0O/R0F7mgk9LsdbKwjIFy1pZBMG/GuIeVL+zlTIRpeho0YEmHzM0H2lrc5a9KoQep2SdikY5YgfboRi0DUhRvvj4hg0AoeBjc5BT71SbC1kMhiROjXwESfoUcWPIAj7WKxXpF+JiSjwwsAnjuucrfvAdT/qWvfRtUC4tkI/qwbg3fAobf6MIjus8bYp1oyJU6QbGdY8NcZ+4ou39y0a5LVRHz89Bf/nPhwcui/ksT99w58ee4F9vWX4CUEsBAhQDFAAAAAgAAAA/AGFdSTpPAQAAjwQAABMAAAAAAAAAAAAAAICBAAAAAFtDb250ZW50X1R5cGVzXS54bWxQSwECFAMUAAAACAAAAD8A8p9J2ukAAABLAgAACwAAAAAAAAAAAAAAgIGAAQAAX3JlbHMvLnJlbHNQSwECFAMUAAAACAAAAD8ARHVb8OgAAAC5AgAAGgAAAAAAAAAAAAAAgIGSAgAAeGwvX3JlbHMvd29ya2Jvb2sueG1sLnJlbHNQSwECFAMUAAAACAAAAD8AzgFzhmwBAAALAwAAGAAAAAAAAAAAAAAAgIGyAwAAeGwvd29ya3NoZWV0cy9zaGVldDEueG1sUEsBAhQDFAAAAAgAAAA/AIMYaiVIAQAAJgIAAA8AAAAAAAAAAAAAAICBVAUAAHhsL3dvcmtib29rLnhtbFBLAQIUAxQAAAAIAAAAPwBcdNNwogAAAOsAAAAUAAAAAAAAAAAAAACAgckGAAB4bC9zaGFyZWRTdHJpbmdzLnhtbFBLAQIUAxQAAAAIAAAAPwBproQY+wEAAD0FAAANAAAAAAAAAAAAAACAgZ0HAAB4bC9zdHlsZXMueG1sUEsBAhQDFAAAAAgAAAA/ABj6RlSwBQAAUhsAABMAAAAAAAAAAAAAAICBwwkAAHhsL3RoZW1lL3RoZW1lMS54bWxQSwECFAMUAAAACAAAAD8A05PycyQBAABQAgAAEQAAAAAAAAAAAAAAgIGkDwAAZG9jUHJvcHMvY29yZS54bWxQSwECFAMUAAAACAAAAD8AXrqn03cBAAAQAwAAEAAAAAAAAAAAAAAAgIH3EAAAZG9jUHJvcHMvYXBwLnhtbFBLBQYAAAAACgAKAIACAACcEgAAAAA='
The body above is the result of the following code:
import base64
f = open(file_path, 'rb')
file_content = f.read()
return base64.b64encode(file_content).decode('UTF-8')
The the code itself:
from django.core.files.base import ContentFile
from post_office import mail
attachments = [{
'name': 'example.xlsx',
'body': ContentFile(body),
'mimetype': 'application/vnd.ms-excel'}]
mail.send(to, from, attachments=attachments, **params)
The problem is that the content in the actual file that is sent by email looks like this:
What could be the reason for that ?
ContentFile has no clue that the content you're passing it is base 64 encoded. You need to base-64-decode it first to pass the actual raw bytes of the Excel file to it.
I am using Django default_storage API to save my media files.
I am able to save the file, and open the file for writing. But I am not able to download the file.
I used the code below to save the file:
default_storage.save(filename, ContentFile(str(a).encode()))
Is there any way to download the file in the same way?
I used the code below to download the file, but it is not either downloading or not throwing any error:
with default_storage.open(filepath, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response['Content-Disposition'] = 'inline ; filename=' +os.path.basename(filepath)
return response
raise Http404
You are on the right path.
with default_storage.open(filepath, 'rb') as fh:
with open('my_local_file','wb') as wh:
data = fh.read() # You may want to split this into chunks..
wh.write(data)
I am trying to generate and output PDF from a django view. I followed the example in django documentation using ReportLab but the downloaded PDF is not opening in any PDF readers.
I use Python 3.7.0, Django==2.1.3, reportlab==3.5.12. I tried adding content_type="application/pdf" to 'FileResponse` but still having the same issue.
import io
from django.http import FileResponse
from reportlab.pdfgen import canvas
def printPDF(request):
# Create a file-like buffer to receive PDF data.
buffer = io.BytesIO()
# Create the PDF object, using the buffer as its "file."
p = canvas.Canvas(buffer)
p.drawString(100, 100, "Hello world.")
p.showPage()
p.save()
return FileResponse(buffer, as_attachment=True, filename='hello.pdf')
The generated PDF should be opening in all PDF readers. But I am getting 'Failed to load PDF document.'
buffer = BytesIO() is used instead of a file to store the pdf document. Before returning it with FileResponse you need to reset the stream position to its start:
buffer.seek(io.SEEK_SET)
Now the pdf download should work as expected.
There seems to be something fishy going on with the interaction of BytesIO and FileResponse. The following worked for me.
def printPDF(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'
p = canvas.Canvas(response)
p.drawString(100, 100, "Hello world.")
p.showPage()
p.save()
return response
I have a Django application with a view that accepts a file to be uploaded. Using the Django REST framework I'm subclassing APIView and implementing the post() method like this:
class FileUpload(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request, *args, **kwargs):
try:
image = request.FILES['image']
# Image processing here.
return Response(status=status.HTTP_201_CREATED)
except KeyError:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'detail' : 'Expected image.'})
Now I'm trying to write a couple of unittests to ensure authentication is required and that an uploaded file is actually processed.
class TestFileUpload(APITestCase):
def test_that_authentication_is_required(self):
self.assertEqual(self.client.post('my_url').status_code, status.HTTP_401_UNAUTHORIZED)
def test_file_is_accepted(self):
self.client.force_authenticate(self.user)
image = Image.new('RGB', (100, 100))
tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
image.save(tmp_file)
with open(tmp_file.name, 'rb') as data:
response = self.client.post('my_url', {'image': data}, format='multipart')
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
But this fails when the REST framework attempts to encode the request
Traceback (most recent call last):
File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 104, in force_text
s = six.text_type(s, encoding, errors)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 118: invalid start byte
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/vagrant/webapp/myproject/myapp/tests.py", line 31, in test_that_jpeg_image_is_accepted
response = self.client.post('my_url', { 'image': data}, format='multipart')
File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site- packages/rest_framework/test.py", line 76, in post
return self.generic('POST', path, data, content_type, **extra)
File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/rest_framework/compat.py", line 470, in generic
data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET)
File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 73, in smart_text
return force_text(s, encoding, strings_only, errors)
File "/home/vagrant/.virtualenvs/myapp/lib/python3.3/site-packages/django/utils/encoding.py", line 116, in force_text
raise DjangoUnicodeDecodeError(s, *e.args)
django.utils.encoding.DjangoUnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 118: invalid start byte. You passed in b'--BoUnDaRyStRiNg\r\nContent-Disposition: form-data; name="image"; filename="tmpyz2wac.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\xff\xd8\xff[binary data omitted]' (<class 'bytes'>)
How can I make the test client send the data without attempting to decode it as UTF-8?
When testing file uploads, you should pass the stream object into the request, not the data.
This was pointed out in the comments by #arocks
Pass { 'image': file} instead
But that didn't full explain why it was needed (and also didn't match the question). For this specific question, you should be doing
from PIL import Image
class TestFileUpload(APITestCase):
def test_file_is_accepted(self):
self.client.force_authenticate(self.user)
image = Image.new('RGB', (100, 100))
tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
image.save(tmp_file)
tmp_file.seek(0)
response = self.client.post('my_url', {'image': tmp_file}, format='multipart')
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
This will match a standard Django request, where the file is passed in as a stream object, and Django REST Framework handles it. When you just pass in the file data, Django and Django REST Framework interpret it as a string, which causes issues because it is expecting a stream.
And for those coming here looking to another common error, why file uploads just won't work but normal form data will: make sure to set format="multipart" when creating the request.
This also gives a similar issue, and was pointed out by #RobinElvin in the comments
It was because I was missing format='multipart'
Python 3 users: make sure you open the file in mode='rb' (read,binary). Otherwise, when Django calls read on the file the utf-8 codec will immediately start choking. The file should be decoded as binary not utf-8, ascii or any other encoding.
# This won't work in Python 3
with open(tmp_file.name) as fp:
response = self.client.post('my_url',
{'image': fp},
format='multipart')
# Set the mode to binary and read so it can be decoded as binary
with open(tmp_file.name, 'rb') as fp:
response = self.client.post('my_url',
{'image': fp},
format='multipart')
You can use Django built-in SimpleUploadedFile:
from django.core.files.uploadedfile import SimpleUploadedFile
class TestFileUpload(APITestCase):
...
def test_file_is_accepted(self):
...
tmp_file = SimpleUploadedFile(
"file.jpg", "file_content", content_type="image/jpg")
response = self.client.post(
'my_url', {'image': tmp_file}, format='multipart')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
It's not so simple to understand how to do it if you want to use the PATCH method, but I found the solution in this question.
from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
with open(tmp_file.name, 'rb') as fp:
response = self.client.patch(
'my_url',
encode_multipart(BOUNDARY, {'image': fp}),
content_type=MULTIPART_CONTENT
)
For those in Windows, the answer is a bit different. I had to do the following:
resp = None
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp_file:
image = Image.new('RGB', (100, 100), "#ddd")
image.save(tmp_file, format="JPEG")
tmp_file.close()
# create status update
with open(tmp_file.name, 'rb') as photo:
resp = self.client.post('/api/articles/', {'title': 'title',
'content': 'content',
'photo': photo,
}, format='multipart')
os.remove(tmp_file.name)
The difference, as pointed in this answer (https://stackoverflow.com/a/23212515/72350), the file cannot be used after it was closed in Windows. Under Linux, #Meistro's answer should work.
Attached is the code which downloads a file from browser using django 1.3 and Apache 2.2 with mod_xsendfile
#login_required
def sendfile(request, productid):
path = settings.RESOURCES_DIR
filepath = os.path.join('C:/workspace/y/src/y/media/audio/','sleep_away.mp3')
print "filepath",filepath
filename = 'sleep_away.mp3' # Select your file here.
print "Within sendfile size", os.path.getsize(filepath)
wrapper = FileWrapper(open(filepath,'r'))
content_type = mimetypes.guess_type(filename)[0]
response = HttpResponse(wrapper, content_type = content_type)
print "Within wrapper"
from django.utils.encoding import smart_str
response['X-Sendfile'] = smart_str(filepath)
response['Content-Length'] = os.path.getsize(filepath)
from django.utils.encoding import smart_str
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(filename)
return response
The console shows the following filesize which is the right size
Within sendfile size 4842585
But when I download/save the file it shows 107 KB...i.e 109,787 bytes.Where am I going wrong. Why isnt it downloading the complete file?
I consider your new to django or python. Try to put the import statements at the beginning of the method. Once imported it can be used through the method no need import every time you use. In windows you should use "rb" (read binary) to serve anything other than text files. Try not to use variable names that might conflict with method names or other keywords of the language. Your method should be like this
#login_required
def sendfile(request, productid):
from django.utils.encoding import smart_str
##set path and filename
resource_path = settings.RESOURCES_DIR # resource dir ie /workspace/y/src/y/media
filename = "sleep_away.mp3" #file to be served
##add it to os.path
filepath = os.path.join(resource_path,"audio",filename)
print "complete file path: ", filepath
##filewrapper to server in size of 8kb each until whole file is served
file_wrapper = FileWrapper(file(filepath,'rb')) ##windows needs rb (read binary) for non text files
##get file mimetype
file_mimetype = mimetypes.guess_type(filepath)
##create response with file_mimetype and file_wrapper
response = HttpResponse(content_type=file_mimetype, file_wrapper)
##set X-sendfile header with filepath
response['X-Sendfile'] = filepath ##no need for smart_str here.
##get filesize
print "sendfile size", os.stat(filepath).st_size
response['Content-Length'] = os.stat(filepath).st_size ##set content length
response['Content-Disposition'] = 'attachment; filename=%s/' % smart_str(filename) ##set disposition
return response ## all done, hurray!! return response :)
Hope that helps
You could have a look at the django-private-files project. Haven't tested it myself, but it looks promissing.
link to the docs --> http://readthedocs.org/docs/django-private-files/en/latest/usage.html
cheers