Headers content included in file uploaded in django rest framework - django

I'm having an issue in my django api file upload.
When I upload file using FileUploadParser everything goes well
but the file upload contains the header of the request stuff like Content-Disposition
when i try to open the uploaded file it is broken. I searched for a while for some solution but no chance. Decided to use MultiPartparser but this way nothing is included in the request.data dict. How can I go around this ? Can somebody show me a code or a way to successfully upload file or image to my api without having them broken ? Thanks for any hint.
Here's the code I have so far
class EstablishmentMediaUploadView(views.APIView):
permission_classes = (IsAuthenticated,)
authentication_class = JSONWebTokenAuthentication
parser_classes = (FileUploadParser,)
serializer_class = MediaSerializer
name = 'establishment-media-file-upload'
def put(self, request, **kwargs):
print(request.data)
if 'file' not in request.data:
raise ParseError("Empty media file for establishment")
establishmentid = kwargs.get('establishmentid')
if establishmentid is None:
return Response({"error": "You didn't specify the establishmentid"}, status=400)
mediaFile = request.data.get('file')
media = Media.objects.create(mediatitle=mediaFile.name)
establishment = Establishment.objects.get(id=establishmentid)
media.establishmentlogo.save(mediaFile.name, mediaFile, save=False)
media.establishment = establishment
media.save()
return Response({"message": "Logo added for this establishment"}, status=200)
I first test it by uploading from Insomnia api test client and Vscode thunder client extension. For both the headers are included.
Then I did the test in my angular frontend. Here's the code of the service method in charge of the upload :
setEstablishmentLogo(establishment: Establishment, media: Media): Observable<Object> {
let formdata = new FormData();
formdata.set("establishmentlogo", media.establishmentlogo);
var url = `${endpoints.establishment_media_upload_uri_base}/${establishment.id}`;
console.log(url)
return this.http.put(url, formdata, {
headers: {
"Accept": "*/*",
"Content-Disposition": `attachment; filename=${media.establishmentlogo.name}`,
'Authorization': `Bearer ${token}`
}
});
}
Got the same behavior. Perhaps I'm getting something wrong in all this. But can't figure it out.

Well i use flutter framework and i upload images from mobile to django and guess what, it updates image field without doing anything from my end. Just make sure where ever you are uploading this image from do it accordingly to http library in that framework.

So just to avoid this headache to another folk. Here is what I found finally.
I was using FileUploadParser in my uploadview. But the point is that when using the 'FileUploadParser' the request.data dict
is populated with the uploaded content. The uploaded content in that case, when it comes from clients like Postman
or Insomnia or Thunder Client of vscode, it contains the single file you uploaded (We assume that you select the option binary request
in those clients) and that way, nothing wrong happens, the files are safe. But when you upload files from the browser, let's say from angular and the parser_class in the view is set to FileUploadParser
then you will successfully upload the file but it would be broken because the whole request (file + headers and browser boundaries stuffs) is parsed as a single file
by FileUploadParser(It's indeed it's job) and thus you end up with broken files on your server or backend. So the way to go around that is to set parser_classes to MultipartParser and FormParser optionally
That way your uploaded files are fine. And much, you don't need to specify Content-Disposition: attachment; filename='some file name' in your request header. Bear in mind that when you use FileUploadParser, your file is in request.data['file'] and when it is MultiPartParser (and FormParser) the file is in request.FILES['file'].
With all this said, the working version of the upload view I posted in my question looks like below :
class EstablishmentMediaUploadView(views.APIView):
permission_classes = (IsAuthenticated,)
authentication_class = JSONWebTokenAuthentication
parser_classes = (MultiPartParser, FormParser,)
name = 'mtp-establishment-media-file-upload'
def post(self, request, *args, **kwargs):
if 'file' not in request.FILES:
return Response({"message": "Please provide a file"}, status=status.HTTP_400_BAD_REQUEST)
establishmentid = kwargs.get('establishmentid')
if establishmentid is None:
return Response({"error": "You didn't specify the establishmentid"}, status=status.HTTP_400_BAD_REQUEST)
mediaFile = request.FILES['file']
media = Media.objects.create(mediatitle=mediaFile.name)
media.establishmentlogo.save(mediaFile.name, mediaFile, save=True)
try:
establishment = Establishment.objects.get(id=establishmentid)
except Establishment.DoesNotExist:
return Response({"error": "Establishment does not exist"}, status=status.HTTP_404_NOT_FOUND)
media.establishment = establishment
media.save()
return Response({"message": "Logo added for this establishment"}, status=status.HTTP_201_CREATED)

Related

Dajngo CSV FIle not download ? When we have a large CSV file download its takes some time?Django 502 bad gateway nginx error Django

How can I download a large CSV file that shows me a 502 bad gateway error?
I get this solution I added in below.
Actually, in this, we use streaming references. In this concept for example we download a movie it's will download in the browser and show status when complete this will give the option to show in a folder same as that CSV file download completely this will show us.
There is one solution for resolving this error to increase nginx time but this is will affect cost so better way to use Django streaming. streaming is like an example when we add a movie for download it's downloading on the browser. This concept is used in Django streaming.
Write View for this in Django.
views.py
from django.http import StreamingHttpResponse
503_ERROR = 'something went wrong.'
DASHBOARD_URL = 'path'
def get_headers():
return ['field1', 'field2', 'field3']
def get_data(item):
return {
'field1': item.field1,
'field2': item.field2,
'field3': item.field3,
}
class CSVBuffer(object):
def write(self, value):
return value
class Streaming_CSV(generic.View):
model = Model_name
def get(self, request, *args, **kwargs):
try:
queryset = self.model.objects.filter(is_draft=False)
response = StreamingHttpResponse(streaming_content=(iter_items(queryset, CSVBuffer())), content_type='text/csv', )
file_name = 'Experience_data_%s' % (str(datetime.datetime.now()))
response['Content-Disposition'] = 'attachment;filename=%s.csv' % (file_name)
except Exception as e:
print(e)
messages.error(request, ERROR_503)
return redirect(DASHBOARD_URL)
return response
urls.py
path('streaming-csv/',views.Streaming_CSV.as_view(),name = 'streaming-csv')
For reference use the below links.
https://docs.djangoproject.com/en/4.0/howto/outputting-csv/#streaming-large-csv-files
GIT.
https://gist.github.com/niuware/ba19bbc0169039e89326e1599dba3a87
GIT
Adding rows manually to StreamingHttpResponse (Django)

File : "Upload a valid image" using Postman in Django Rest Framework

I am using Django Rest Framework to upload profile picture and Google Cloud Storage to store my images. I test my API with Postman and i have this result :
Postman result
And here is my Postman headers : Postman headers
This is my code :
class ProfilePictureSerializer(serializers.Serializer):
file = serializers.ImageField()
class UploadProfilePictureAPI(APIView):
permission_classes = (IsAuthenticated,)
parser_classes = [FileUploadParser]
#staticmethod
def post(request):
input_serializer = serializers.ProfilePictureSerializer(
data=request.data
)
input_serializer.is_valid(raise_exception=True)
profile = ProfileService.upload_profile_picture(
request.user,
**input_serializer.validated_data
)
output_serializer = serializers.ProfileSerializer(profile)
return Response(
output_serializer.data,
status=status.HTTP_202_ACCEPTED
)
#staticmethod
def upload_profile_picture(user, file):
user.profile.profile_picture = file
user.profile.save()
return user.profile
path(
'upload/picture',
views.UploadProfilePictureAPI.as_view(),
name='api_upload_profile_picture'
),
I does not understand why I have this response. Could you help me ?
I think the issue here might be that FileUploadParser expects you to just send raw binary data (you can see the binary option in Postman).
In your current example, can you just try to send the binary data like so?
For multipart form you should use the MultiPartParser: https://www.django-rest-framework.org/api-guide/parsers/#multipartparser

django: how to correctly specify output-download file-type (in this case mp3)?

I have a simple django platform where I can upload text files. Ultimately I want to return a downloadable mp3 audio file made from the text in the uploaded file. My problem currently is that I cannot seem to correctly specify the type of file that the website outputs for download.
I then tried to make the downloadable output of the website an mp3 file:
views.py (code adapted from https://github.com/sibtc/simple-file-upload)
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
myfile = request.FILES['myfile']
print(str(request.FILES['myfile']))
x=str(myfile.read())
tts = gTTS(text=x, lang='en')
response=HttpResponse(tts.save("result.mp3"),content_type='mp3')
response['Content-Disposition'] = 'attachment;filename=result.mp3'
return response
return render(request, 'core/simple_upload.html')
Upon pressing the upload button, the text-to-speech conversion is successful but the content_type of the response is not definable as 'mp3'. The file that results from the download is result.mp3.txt and it contains 'None'.
Can you try to prepare your response using the sample code below?
I've managed to return CSV files correctly this way so it might help you too.
Here it is:
HttpResponse(content_type='text/plain') # Plain text file type
response['Content-Disposition'] = 'attachment; filename="attachment.txt"' # Plain text file extension
response.write("Hello, this is the file contents.")
return response
There are two problems I can see here. The first is that tts.save() returns None, and that is getting passed directly to the HttpResponse. Secondly, the content_type is set to mp3 and ought to be set to audio/mp3.
After calling tts.save(), open the mp3 and pass the file handle to the HttpResponse, and then also set the content_type correctly - for example:
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
...
tts.save("result.mp3")
response=HttpResponse(open("result.mp3", "rb"), content_type='audio/mp3')

How to force download an image on click with django and aws s3

I have this view, which takes a user_id and image_id. When the user cliks the link, check if there is an image. If there is, then I would like the file to force download automatically.
template:
<a class="downloadBtn" :href="website + '/download-image/'+ user_id+'/'+ image_id +'/'">Download</a>
Before I was developing it in my local machine, and this code was working.
#api_view(['GET'])
#permission_classes([AllowAny])
def download_image(request, user_id=None, image_id=None):
try:
ui = UserImage.objects.get(user=user_id, image=image_id)
content_type = mimetypes.guess_type(ui.image.url)
wrapper = FileWrapper(open(str(ui.image.file)))
response = HttpResponse(wrapper, content_type=content_type)
response['Content-Disposition'] = 'attachment; filename="image.jpeg'
return response
except UserImage.DoesNotExist:
...
But now I am using aws s3 for my static and media files. I am using django-storages and boto3. How can I force download the image in the browser?
#api_view(['GET'])
#permission_classes([AllowAny])
def download_image(request, user_id=None, image_id=None):
try:
ui = UserImage.objects.get(user=user_id, image=image_id)
url = ui.image.url
...
... FORCE DOWNLOAD THE IMAGE
...
except UserImage.DoesNotExist:
...
... ERROR, NO IMAGE AVAILABLE
...
You can just return a HttpResponse with the image itself.
return HttpResponse(instance.image, content_type="image/jpeg")
This will return the image's byte stream. The Content-type header is to show the images in platforms like Postman.

Using nested objects with Django Rest Framework and unit tests

I wrote several unit tests on my Django Rest Framework endpoints without any trouble, until I tried to pass nested object in a POST request:
class BookTestCase(APIVersion, APITestCase):
def setUp(self):
self.url = self.reverse_with_get_params('book')
self.user = CustomerFactory.create().user
self.base_data = {"foo": "bar",
"credit_card": {"card_number": "1234567812345678",
"expiration_date": "1116",
"security_code": "359"},
"foo2": "bar2"}
def test_book(self):
add_token_to_user(self.user, self.client)
response = self.client.post(self.url, self.base_data)
self.assertEqual(response.status_code, 200)
Then, runing the related web service with a pdb.set_trace() at the very beginning, here is the content of request.DATA:
<QueryDict: {u'foo': [u'bar'],
u'credit_card': [u'expiration_date', u'security_code', u'card_number'],
u'foo2': [u'bar2']}>
As you can see, every level1 object is correctly filled, but credit card content has disapeared.
Any idea? Thanks!
Note: Django 1.6 / Rest Framework 2
You have to change to format of your post call. Try format='json'
response = self.client.post(self.url, self.base_data, format='json')