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

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.

Related

django AWS S3 download optimisation

I work on some django + DRF project, that has application for media files upload to and download from AWS S3 (application is something like proxy, using to not access frontend to download images directly from AWS S3). I noticed, that my download requests work not so fast (500ms +/- 100ms), and in production it probably will be a problem, so my question is following: "Is there a way to make these requests faster or separate download logic to some async microservice or multiprocess task? What is the best practice?"
Service, that download images for me in current project state (for context):
# media_app/services/download/image.py
class ImageDownloadService(FileDownloadServiceBase):
model = Image
# media_app/services/download/base.py
class FileDownloadServiceBase:
model = ...
def __init__(self, instance: str) -> None:
self.instance = instance
def _get_file(self, presigned_url):
response = requests.get(url=presigned_url, stream=True)
return response
def download(self) -> Tuple[file_data, status_code]:
s3 = boto3.resource(
service_name='s3',
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_S3_REGION_NAME,
)
url = s3.meta.client.generate_presigned_url(
ClientMethod="get_object", ExpiresIn=3600,
Params={
"Bucket": settings.AWS_STORAGE_BUCKET_NAME,
"Key": f'media/public/{self.instance.file_name}',
},
)
response = self._get_file(url)
return response.content, response.status_code

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)

Headers content included in file uploaded in django rest framework

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)

Create download link file in django

I created a file in project, generation pdf from html. For this i have this method:
def generation_html_to_pdf(self):
path_pdf = None
with NamedTemporaryFile(delete=False, suffix=".pdf", dir='pdf_files') as tf:
path_pdf = tf.name
pdfkit.from_file('templates/first_page.html', tf.name)
return path_pdf
Then, in pdf_files folder i have the pdf file. I want to get a download link for this file:
my view
path_to_pdf = generation_html_to_pdf()
download_link = 'http://' + request.get_host() + path_to_pdf
json_inf_pdf = {'download_link': download_link}
return JsonResponse(json_inf_pdf, status=200)
i have json like this:
{"download_link": "http://127.0.0.1:8000/home/alex/projects/test_project/pdf_files/tmpe0nqbn01.pdf"}"
when i click in this link i have error:
Page not found (404)
You need to create download view and url. Function like this to create link:
def download_link(request):
''' Create download link '''
download_link = 'http://{}/{}'.format(request.get_host(), 'download/my_filename')
json_inf_pdf = {'download_link': download_link}
return JsonResponse(json_inf_pdf, status=200)
and to download pdf:
def download_file(request, my_filename):
''' Download file '''
# Open template
from django.conf import settings
template_url = os.path.join(settings.BASE_DIR, 'templates', 'first_page.html')
template_open = open(template_url, 'r')
# Read template
from django import template
t = template.Template(template_open.read())
c = template.Context({})
# Create pdf
pdf = pdfkit.from_string(t.render(c))
# Create and return response with created pdf
response = HttpResponse(pdf)
response['Content-Type'] = 'application/pdf'
response['Content-disposition'] = 'attachment ; filename = {}'.format(my_filename)
return response
and url:
path('/download/<str:my_filename>', views.download_file, name="download_pdf')
I can't guarantee that this will work in your case without modification, since I can't tell which html-to-pdf library you're using and without seeing your other code. It's just a basic implementation idea.

Download image data then upload to Google Cloud Storage

I have a Flask web app that is running on Google AppEngine. The app has a form that my user will use to supply image links. I want to download the image data from the link and then upload it to a Google Cloud Storage bucket.
What I have found so far on Google's documentation tells me to use the 'cloudstorage' client library which I have installed and imported as 'gcs'.
found here: https://cloud.google.com/appengine/docs/python/googlecloudstorageclient/read-write-to-cloud-storage
I think I am not handling the image data correctly through requests. I get a 200 code back from the Cloud Storage upload call but there is no object when I look for it in the console. Here is where I try to retrieve the image and then upload it:
img_resp = requests.get(image_link, stream=True)
objectName = '/myBucket/testObject.jpg'
gcs_file = gcs.open(objectName,
'w',
content_type='image/jpeg')
gcs_file.write(img_resp)
gcs_file.close()
edit:
Here is my updated code to reflect an answer's suggestion:
image_url = urlopen(url)
content_type = image_url.headers['Content-Type']
img_bytes = image_url.read()
image_url.close()
filename = bucketName + objectName
options = {'x-goog-acl': 'public-read',
'Cache-Control': 'private, max-age=0, no-transform'}
with gcs.open(filename,
'w',
content_type=content_type,
options=options) as f:
f.write(img_bytes)
f.close()
However, I am still getting a 201 response on the POST (create file) call and then a 200 on the PUT call but the object never appears in the console.
Try this:
from google.appengine.api import images
import urllib2
image = urllib2.urlopen(image_url)
img_resp = image.read()
image.close()
objectName = '/myBucket/testObject.jpg'
options = {'x-goog-acl': 'public-read',
'Cache-Control': 'private, max-age=0, no-transform'}
with gcs.open(objectName,
'w',
content_type='image/jpeg',
options=options) as f:
f.write(img_resp)
f.close()
And, why restrict them to just entering a url. Why not allow them to upload a local image:
if isinstance(image_or_url, basestring): # should be url
if not image_or_url.startswith('http'):
image_or_url = ''.join([ 'http://', image_or_url])
image = urllib2.urlopen(image_url)
content_type = image.headers['Content-Type']
img_resp = image.read()
image.close()
else:
img_resp = image_or_url.read()
content_type = image_or_url.content_type
If you are running on the development server, the file will be uploaded into your local datastore. Check it at:
http://localhost:<your admin port number>/datastore?kind=__GsFileInfo__
and
http://localhost:<your admin port number>/datastore?kind=__BlobInfo__