Stream file from remote url to Django view response - django

Is there any way to stream file from remote URL with Django Response (without downloading the file locally)?
# view.py
def file_recover(request, *args, **kwargs):
file_url = "http://remote-file-storage.com/file/111"
return StreamFileFromURLResponse(file_url)
We have file storage (files can be large - 1 GB and more). We can't share download url (there are security issues). File streaming can significantly
increase download speed by forwarding download stream to Django response.

Django has built in StreamingHttpResponse class which should be given an iterator that yields strings as content. In example below I'm using requests Raw Response Content
import requests
from django.http import StreamingHttpResponse
def strem_file(request, *args, **kwargs):
r = requests.get("http://host.com/file.txt", stream=True)
resp = StreamingHttpResponse(streaming_content=r.raw)
# In case you want to force file download in a browser
# resp['Content-Disposition'] = 'attachment; filename="saving-file-name.txt"'
return resp

Related

Download a file without storing it

I'm building a Youtube Video Downloader using PyTube and Flask. What I want to do is the end user receives the video file, but I never store it into the server.
Currently the code looks like this:
def download_video():
if request.method == "POST":
url = YouTube(session['link']) # Getting user input
itag = request.form.get("itag") # Getting user input
video = url.streams.get_by_itag(itag)
file = video.download(path) # Downloading the video into a folder
return send_file(file, as_attachment=True)
return redirect(url_for("home"))
The code works fine, the only drawback is that it is being store into the server, which later on can become an issue.
I already tried to download it to /dev/null/, which locally seems to work, but when deployed to Heroku, it gives me an Internal Server Error.
The download method is used to:
Write the media stream to disk.
A dirty workaround could of course be to remove the file saved at the output_path after calling download, but you could also write the media stream to a buffer using stream_to_buffer and send that.
Minimal and reproducible example
from pytube import YouTube
from flask import Flask, send_file
from io import BytesIO
app = Flask(__name__)
#app.route("/")
def index():
buffer = BytesIO()
url = YouTube("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
video = url.streams.get_by_itag(18)
video.stream_to_buffer(buffer)
buffer.seek(0)
return send_file(
buffer,
as_attachment=True,
attachment_filename="cool-video.mp4",
mimetype="video/mp4",
)
if __name__ == "__main__":
app.run()

how to prompt user to save a pdf file to his local machine in django?

I am fairly new to Django and my project requires me to prompt user to open a pdf upon clicking a link. I already have the pdf file on my local machine and dont want to recreate it using Reportlab. Is there any way to do it?
I tried
with open("/user/some/directory/somefilename.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
return response
but it returned 404 page not found as the requested url wasn't in the URLconf of myproject.urls
What am I missing?
In general, when user click "Download", you can:
- If file is not existed:
- Generate pdf file use ReportLab as you did.
- Store generated file to a public dir.
return HttpResponseRedirect(file_url_to_public_dir)
The way that worked for me is by using FileSystemStorage
from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse
fs = FileSystemStorage("/Users/location/where/file/is_saved/")
with fs.open("somefile.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"'
return response
and now its prompting the user to save the file as it normally would!

Error sending a file using Django - file turns out empty

This is my views.py files:
from django.http import HttpResponse
def render(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
response['X-Sendfile'] = '/files/filename.pdf'
# path relative to views.py
return response
When I run the server and request
http://localhost:8080/somestring
I get an empty file called somefilename.pdf. I suspect that there is some crucial part missing in render.
The other parts of this app outside of views.py are correct to my understanding.
Here is the code that solved my problem:
from django.http import HttpResponse
from wsgiref.util import FileWrapper
def render(request):
response = HttpResponse(FileWrapper(open('file.pdf', 'rb')), content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
return response
The manage.py runserver development serer doesn't support X-Sendfile. In production, you need to enable X-Sendfile for your server (e.g. Apache).
You may find the django-sendfile package useful. It has a backend that you can use in development. However, it hasn't had a release in some time, and I found that I had to apply pull request 62 to get Python 3 support.

django zip file doesn't get downloaded

I want to download zip files using Django views. I have gone through many solutions in stack overflow. But the file does not get downloaded at all. Here is the code I am using.
Could anyone, tell me where I am going wrong.
response = HttpResponse(mimetype='application/zip')
response['Content-Disposition'] = 'attachment; filename=%s' % doc[Zip_file_name]
response['X-Sendfile'] = "./Zipfiles" # the path where the zip files are stored
return response
In chrome, If I use inspect element, and double click on the url shown in the network tab, the file gets downloaded as it is recognized as a http get request, whereas on button click nothing happens.
Please Help.
from django.http import HttpResponse
from django.core.servers.basehttp import FileWrapper
# file
response = HttpResponse(FileWrapper(myfile), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=myfile.zip'
return response

How to return static files passing through a view in django?

I need to return css files and js files according to specific logic. Clearly, static serve does not perform what I need. I have a view, whose render method uses logic to find the proper file, but then I have to return it. Technically, I can just read the file and stuff it into a HttpResponse object with the proper mime type, but I was wondering if there was a better strategy. (like fpassthru() in php)
This is what I used:
test_file = open('/home/poop/serve/test.pdf', 'rb')
response = HttpResponse(content=test_file)
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' \
% 'whatever'
return response
What webserver software are you using?
At least for Apache and NginX, there is a module enabling you to use the X-SendFile HTTP header. The NginX website says Lighty can do this, too.
In your wrapper view:
...
abspath = '/most_secret_directory_on_the_whole_filesystem/protected_filename.css'
response = HttpResponse()
response['X-Sendfile'] = abspath
response['Content-Type'] = 'mimetype/submimetype'
# or let your webserver auto-inject such a header field
# after auto-recognition of mimetype based on filename extension
response['Content-Length'] = <filesize>
# can probably be left out if you don't want to hassle with getting it off disk.
# oh, and:
# if the file is stored via a models.FileField, you just need myfilefield.size
response['Content-Disposition'] = 'attachment; filename=%s.css' \
% 'whatever_public_filename_you_need_it_to_be'
return response
Then you can connect the view via http://mysite.com/url_path/to/serve_hidden_css_file/.
You can use it anytime you need to do something upon a file being requested that should not be directly accessible to users, like limiting who can access it, or counting requests to it for stats, or whatever.
For Apache: http://tn123.ath.cx/mod_xsendfile/
For NginX: http://wiki.nginx.org/NginxXSendfile
Why don't you use Django staticfiles inside your view
from django.contrib.staticfiles.views import serve
...
def view_function(request):
return serve(request, 'absolute_path_to_file_name')
Why not return an HttpResponseRedirect to the location of the correct static file?
Serving files directly from a view is very slow. If you are looking for normal file serving see this question: Having Django serve downloadable files
To very easily serve files through a view (for debug purposes, for example) keep reading.
# In your urls.py:
url(r'^test-files/(?P<name>.+)/$', views.test_files, name='test_files'),
# In your views.py:
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt # (Allows file download with POST requests, can be omitted)
def test_files(request, name):
if name == "myxml":
fsock = open("/djangopath/data/static/files/my.xml", "rb")
return HttpResponse(fsock)
This allows you to download the file from: http://127.0.0.1:8080/app/test-files/myxml/
Pass an iterator (such as the result of open()) to the HttpResponse constructor.
you can use below code in your view:
Note:in this function I return images but you can return every thing based your need and set your context_type
from django.http import HttpResponse,Http404
import os
def img_finder(request, img_name):
try:
with open(os.path.dirname(os.path.abspath(__file__)) + '/static/img/' + img_name, 'rb') as f:
return HttpResponse(f.read(), content_type="image/jpeg")
except IOError:
raise Http404
Here the most simple and efficient way to do this.
app/urls.py
from django.urls import re_path
from app import views
urlpatterns = [
re_path(r'^(?P<public_url>.*)$', views.public, name="public"),
]
Warning : put the URL pattern at the end
app/views.py
import os
from django.conf import settings
from django.views.static import serve
def public(request, public_url):
public_folder = os.path.join(str(settings.BASE_DIR), 'folder_path')
return serve(request, public_url, document_root=public_folder)
It should be wasteful to use django to serve static content (not to mention, several orders of magnitude slower).
I'd rather convert the view into a context processor and use the variables in templates to find what blocks to include.