I have an Django app where users can download files that was assigned to them. How can I be sure that only the user who is assigned the file may download that file. Because its in the media dir, any one can brows there, so is there a way of letting only the relevant user download the file?
I did the same thing one year ago.
I want only relevant users can download their photos:
# urls.py
(r'^data/photos/(?P<path>.*)$','views.data_access'),
my view 'data_access' gives the photo, or a 403 page
# view data_access(request, path)
# [...code...]
if user_can_download:
response = HttpResponse(mimetype="image/jpeg")
response['Content-Disposition'] = 'attachment; filename=%s' % unicode(photo.image.name)
response['X-Accel-Redirect'] = '/protected/'+ unicode(path)
return response
else:
return HttpResponseForbidden()
Related
I cant seem to understand how is it possible that for GCS the authenticated URL shows a different image then the public URL ?
Im uploading the images via a python django script
def upload_to_cloud(blob_name, file_obj):
file_type = imghdr.what(file_obj)
blob_name = str(blob_name) + '.' + file_type # concatenate string to create 'file_name.format'
stats = storage.Blob(bucket=bucket, name=blob_name).exists(client) # check if logo with the same reg.nr exists
if stats is True: # if exists then delete before uploading new logo
storage.Blob(bucket=bucket, name=blob_name).delete()
blob = bucket.blob(blob_name)
blob.upload_from_file(file_obj=file_obj, content_type=f'image/{file_type}')
path = blob.public_url
return path
class CompanyProfile(SuccessMessageMixin, UpdateView): # TODO why company logo differs from the one in ads_list?
model = Company
form_class = CompanyProfileCreationForm
def form_valid(self, form):
"""
Check if user uploaded a new logo. If yes
then upload the new logo to google cloud
"""
if 'logo' in self.request.FILES:
blob_name = self.request.user.company.reg_nr # get company registration number
file_obj = self.request.FILES['logo'] # store uploaded file in variable
form.instance.logo_url = upload_to_cloud(blob_name, file_obj) # update company.logo_url with path to uploaded file
company = Company.objects.get(pk=self.request.user.company.pk)
company.save()
return super().form_valid(form)
else:
return super().form_valid(form)
Any ideas on what Im doing wrong and how its even possible? The file that I actually uploaded is the one under authenticated url. The file thats under public url is a file that I uploaded for a different blob
EDIT
Im adding screenshot of the different images because after some time the images appear to be the same as they should be. Some people are confused by this and comment that the images are the same after all
Public URL
Authenticated URL
Note that caching issue is ruled out since I sent the public URL to my friend and he also saw that the image is the HTML text although the image in the authenticated URL (the correct image) was a light bulb. He also noted that the URL preview in fb messenger showed the light bulb image but when he actually opened the URL the HTML text image appeared
This problem persists in case a file is uploaded with the same blob name. This happens regardless if its overwritten by gcs or if I previously execute blob delete function and then create a new file with the same name as the deleted blob.
In general the same object will be served by storage.googleapis.com and storage.cloud.google.com.
The only exception is if there is some caching (either in your browser, in a proxy, with Cloud CDN or in GCS). If you read the object via storage.cloud.google.com before uploading a new version, then reading after by storage.cloud.google.com may serve the old version while storage.googleapis.com returns the new one. Caching can also be location dependent.
If you can't allow an hour of caching, set Cache control to no-cache.
I am generating a report download in a view and starting the download after processing the POST data. That means the user sends a form and the download starts:
views.py
def export(request):
if request.method == 'POST' and 'export_treat' in request.POST:
form1 = TransExport(request.POST, instance= obj)
if form1.is_valid():
...
...
response=HttpResponse(ds.xls,content_type="application/xls")
response['Content-Disposition'] = 'attachment; filename="Report_Behandlungen.xls"'
return response
What I need is a page refresh after the download (or a redirect).
How can I achieve this?
I would just do it in simple logic with javascript:
user clicks the link
/download_file_now/
and comes to /file_downloaded/ where the download starts and after 3 seconds, you just redirect the page via js
location.replace('/another_url/');
to detect if the download is ready is not easy
I am hosting some video files in rackspace cloud files, and each user is allowed to download the files that are assigned to them.
Because of the file sizes it is not feasible to buffer the object in the webserver(webfaction)
I tried a redirect to the file, with Content-Disposition set to attachment, but to no avail.
What kind of options do I have, if any?
Ideally the file download would pop as coming from my domain after clicking a link that points to something like example.com/video/42/download/ so I can handle authentication ect. but im not sure how to structure my view for that to happen.
You are probably best served by using an HttpResponseRedirect unless there is something I am misunderstanding...?
# urls.py
from django.http import HttpResponseRedirect
url(r'^applications/(?P<id>\d+)/image\.png$', 'core.views.serve_image', name='image'),
This will serve a view at http://localhost/application/12345/image.png.
# core/views.py
def serve_application_image(request, id):
# redirect to temp_url
application = Application.objects.get(id=id)
return HttpResponseRedirect(application.image.temp_url)
And this will redirect users that hit that URL to the Rackspace URL. It can work for embedding videos, images, etc, in html <img> tags and such. Browser clients will be able to see the redirected URL (at rackcdn.com).
I have configured my apps to serve a temp_url property that expires after 15 minutes. The temporary URL is created for the CDN at Rackspace.com and their documentation may be out of the scope for this question so I'll leave it off for now... but the code I use to sub-class ImageField to serve image attributes with the .temp_url code follows:
import hmac
from hashlib import sha1
from time import time
class ImageFieldFile_With_Temp_Url(ImageFieldFile):
#property
def temp_url(self):
container_name, file_name = (self.storage.container.name, self.name)
key = settings.CUMULUS['CUSTOM__X_ACCOUNT_META_TEMP_URL_KEY']
public_url = settings.CUMULUS['CUSTOM__X_STORAGE_URL']
method = 'GET'
expires = int(time() + settings.CUMULUS['CUSTOM__X_TEMP_URL_TIMEOUT'])
url = '%s/%s/%s' % (public_url, container_name, file_name)
base_url, object_path = url.split('/v1/')
object_path = '/v1/' + object_path
hmac_body = '%s\n%s\n%s' % (method, expires, object_path)
sig = hmac.new(key, hmac_body, sha1).hexdigest()
return '%s%s?temp_url_sig=%s&temp_url_expires=%s' % (base_url, object_path, sig, expires)
class ImageField_With_Temp_Url(models.ImageField):
attr_class = ImageFieldFile_With_Temp_Url
models.ImageField = ImageField_With_Temp_Url
Note that I am using the django-cumulus project in this approach.
Importing this function anywhere at the top of your models.py will extend ImageField with a new temp_url property (since I assign it to models.ImageField ...).
I'm currently writing a small personal-use website served with nginx, utilizing uwsgi, django, and bootstrap. All is going smoothly except for one issue that I cannot figure out. I have download links (buttons) on the site, and when clicked, should initiate a file download. Here is the view that is executed when a button is pressed:
#login_required
def download_file(request):
'''
downloads a file given a specified UUID
'''
if request.method == 'GET':
file_uuid = request.GET['file_id']
file_row = Files.objects.get(uuid=file_uuid)
file_name = file_row.file_name
response = HttpResponse()
response['Content-Disposition'] = 'attachment; filename=%s' % file_name
response['X-Accel-Redirect'] = '/media/files/%s' % file_name
return response
else:
return redirect('/files')
/media/files is served directly by nginx as a internal location:
location /media/files/ {
internal;
alias /mnt/files/;
}
How this view is being called with an onclick event assigned to each button:
$('.download_btn').on('click',function(){
download_file(this.id);
})
function download_file(uuid){
$('.file_id').val(uuid);
$('.get_file').submit();
}
I have a form with a single hidden field. This gets set to the id (uuid) of the button that is pressed.
Pretty simple right? My issue is that when the download button is pressed, the download is not initiated correctly. The user is not prompted with a save dialog, nor does the file begin automatically downloading (Chrome or Safari). Instead, in debug tools, I can see the file downloading to what seems to be local storage in the browser, or some memory location (these are large files; > 1GB). I see the memory ballooning, and eventually the browser will crash. Any clue what I'm doing wrong here? Based on what I've been reading, this should be working without issue.
I have filefield in my django model:
file=models.FileField(verbose_name=u'附件',upload_to='attachment/%Y/%m/%d',max_length=480)
This file will display in the web page with link "http://test.com.cn/home/projects/89/attachment/2012/02/24/sscsx.txt"
What I want to do is when user click the file link, it will download the file automatically;
Can anyone tell me how to do this in the view?
Thanks in advance!
You can try the following code, assuming that object_name is an object of that model:
filename = object_name.file.name.split('/')[-1]
response = HttpResponse(object_name.file, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
See the following part of the Django documentation on sending files directly: https://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.FileField.storage
All that will be stored in your database is a path to the file (relative to MEDIA_ROOT). You'll most likely want to use the convenience url function provided by Django. For example, if your ImageField is called mug_shot, you can get the absolute path to your image in a template with {{ object.mug_shot.url }}.