in my Django 'views, I create a pdf file and I want to download it.
The file exist (path: /app/data/4.pdf) and i launch this command:
def download_line(request):
if not request.is_ajax() and not request.method == 'GET':
raise Http404
try:
fs =FileSystemStorage('/app/data')
with fs.open('4.pdf') as pdf:
response =HttpResponse(pdf,content_type='application/pdf')
response['Content-Disposition']='attachment; filename="4.pdf"'
except Exception as e:
logger.warning("Download Line | Erreur : " + e.message)
return response
But the download doesn't start and no error. Have you got a solution?
Thanks.
You can download existing file in your app by a link and static, like this
<a href="{% static 'questions/import_files/import_questions.xlsx' %}" download>Excel Format File </a>
I use FileResponse to serve file download, when the file already exists. FileResponse has been around since Django 1.7.4.
from django.core.files.storage import FileSystemStorage
from django.http import FileResponse
def download_line(request):
fs = FileSystemStorage('/absolute/folder/name')
FileResponse(fs.open('filename.pdf', 'rb'), content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename="filename.pdf"'
return response
Try this, I use this lines to download files
from django.http import HttpResponse
from wsgiref.util import FileWrapper
import os
def download(request, file_path):
"""
e.g.: file_path = '/tmp/file.pdf'
"""
try:
wrapper = FileWrapper(open(file_path, 'rb'))
response = HttpResponse(wrapper, content_type='application/force-download')
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
except Exception as e:
return None
def sample_download_client_excel(request):
"""
e.g.: file_path = '/tmp/file.pdf'
"""
try:
obj = SampleFile.objects.all().first()
file_path = obj.file_name.url.strip('/')
wrapper = FileWrapper(open(file_path, 'rb'))
response = HttpResponse(
wrapper,
content_type='application/force-download'
)
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
except Exception as e:
return None
Related
Im trying to generate pdf from given html file but get_template function is not working i guess.
from io import BytesIO
from django.template.loader import get_template
from xhtml2pdf import pisa
def render_to_pdf(context_dict={}):
try:
template = get_template('invoice.html')
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
return result.getvalue()
return None
except Exception as e:
print('ERROR', e)
The Except block returns None.
Change line to:
pdf = pisa.pisaDocument(BytesIO(html.encode("utf-8")), result)
It was my logical error issue. I didn't added 'templates' folder to DIRS in settings.py file.
I am creating a Django API that converts any URL or HTML file into pdf and Docx. The implemented code below already renders in pdf format using pdfkit package. I'm using python-docx to generate in Docx, but I don't know how to handle it. I would like to have any support, please. I don't have deep knowledge and any help will be appreciated.
Here is my convert.py file:
import io
from pydoc import doc
from tempfile import NamedTemporaryFile
from typing import IO
from urllib.parse import urlparse
import pdfkit
from docx import Document
class ConvertingError(Exception):
"""
This exception represents an error during converting.
In example, when Host of a url is unreachable.
In other words, this is a wrapper for wkhtmltopdf errors.
"""
pass
def url_to_pdf(url: str) -> IO:
"""Fetch HTML from url and convert the page to pdf,"""
with NamedTemporaryFile('w+b') as tmpf:
try:
pdfkit.from_url(url, tmpf.name)
except OSError as e:
raise ConvertingError from e
pdf = io.BytesIO(tmpf.read())
return pdf
def html_to_pdf(html: str) -> IO:
"""Convert HTML string to pdf."""
with NamedTemporaryFile('w+b') as tmpf:
try:
pdfkit.from_string(html, tmpf.name)
except OSError as e:
raise ConvertingError from e
pdf = io.BytesIO(tmpf.read())
return pdf
def filename_from_url(url: str) -> str:
"""
Generate pdf filename using a hostname of a URL.
If no hostname is provided, return 'default.pdf' as filename.
"""
parsed = urlparse(URL)
return (parsed.hostname or 'default') + '.pdf'
def url_to_docx(url: str) -> IO:
pass
def html_to_docx(html: str) -> IO:
pass
And my views.py file
from fileinput import filename
from typing import IO
from django.http import FileResponse
from rest_framework.exceptions import ValidationError
from rest_framework.parsers import MultiPartParser
from rest_framework.viewsets import ViewSet
from .converter import filename_from_url, html_to_pdf, url_to_pdf, ConvertingError,
url_to_docx, html_to_docx
from .serializers import HtmlFileInputSerializer, UrlInputSerializer
def generate_from_html(self, request):
serializer = HtmlFileInputSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
file: IO = serializer.validated_data['file']
content = str(file.read())
try:
pdf = html_to_pdf(content)
except ConvertingError:
raise ValidationError('The file is of inappropriate type or corrupted.')
response = FileResponse(pdf)
response["Content-Type"] = 'application/pdf'
return response
def generate_docx_from_html(self, request):
pass
# class UrlConverterViewSet(ViewSet):
def generate_from_url(self, request):
serializer = UrlInputSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
url: str = serializer.validated_data['url']
try:
pdf = url_to_pdf(URL)
except ConvertingError:
raise ValidationError('The url is invalid or unreachable.')
filename = serializer.validated_data.get('filename') or filename_from_url(URL)
response = FileResponse(pdf, filename=filename)
response["Content-Type"] = 'application/pdf'
return response
def generate_docx_from_url(self, request):
pass
class GeneratePdf(ViewSet):
# generate pdf view from html file and URL
parser_classes = (MultiPartParser,)
def create(self, request):
if request.data.get('file'):
return generate_from_html(self, request)
elif request.data.get('url'):
return generate_from_url(self, request)
else:
raise ValidationError('The file or url is invalid or unreachable.')
When I use the zipfile object in multiple functions, it works fine. However, when I try to run the one of the functions in thread, it gives the error "I/O operation on closed file".
Below code works fine which validates and extracts the zipfile
from zipfile import ZipFile
from threading import Thread
def extract_data(file):
zip_file = Zipfile(file)
validate = validate_function(zip_file)
if validate.status_code == 200:
data = extract_function(zip_file)
However, If I run the extract_function in thread, It gives me "ValueError: I/O operation on closed file"
def extract_data(file):
zip_file = Zipfile(file)
validate = validate_function(zip_file)
if validate.status_code == 200:
extract = Thread(target=extract_function,args=[zip_file])
extract.start()
Please guide me for understanding the root cause of this issue.
Update:
Here is the sample code to reproduce the issue:
from zipfile import ZipFile
from threading import Thread
import pandas as pd
from flask import Flask, request, Response
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.datastructures import FileStorage
from flask_restplus import Api, Resource, reqparse, cors
from flask_cors import cross_origin
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
api = Api(app,
version='1.0.0',
doc='/',
)
def validate_function(zip_file):
try:
error = ZipFile.testzip(zip_file)
if error is None:
return Response('Zip file is validated',200)
else:
return Response('Invalid Zip file',601)
except Exception as e:
return Response('Error :' + str(e),601)
def extract_function(zip_file):
df_list = []
try:
for file in zip_file.namelist():
if file.endswith('.csv'):
df_list.append(pd.read_csv(zip_file.open(file)))
else:
excel_df = pd.read_excel(zipfile.open(file),None)
if type(excel_df) == dict:
df_list.extend(list(excel_df.values()))
else:
df_list.append(excel_df)
print(len(df_list))
except Exception as e:
print('Error in converting to dataframe', str(e))
def extract_data(file):
zip_file = ZipFile(file)
resp = validate_function(zip_file)
if resp.status_code == 200:
data = Thread(target= extract_function, args=[zip_file])
data.start()
#extract_function(zip_file) --> This works
return resp
process_data = reqparse.RequestParser()
process_data.add_argument('file', location='files', type=FileStorage, required=True, help='Input file in Zip format')
#api.route('/process-data')
#api.expect(process_data)
class DataExtract(Resource):
#cors.crossdomain(origin='*')
#cross_origin()
def post(self):
file = request.files['file']
resp = extract_data(file)
return resp
app.run()
Use with to open files, so they are closed correctly after you are done. You also have to open the file separately in the new thread:
def extract_data(file):
with ZipFile(file) as zip_file:
validate = validate_function(zip_file)
if validate.status_code == 200:
extract = Thread(target=extract_function,args=[file])
extract.start()
def extract_function(file):
with ZipFile(file) as zip_file:
# extract ...
I have files(docx file) which I need to let users download by clicking on a button.
These files were uploaded to a Model FileField.
This is my views.py
def files_view(request):
files = FileModel.objects.filter(user=request.user)
return render(request, template_name='folder/files_list.html')
and this is my template.html
{% for serving_files in user.files.all %}
<button>Download</button>
I'm doing this but the files are unable to download. What changes do I need to make?
So onclick of button call this function, the code is untested but with minor tweaks this should work.
import os
from django.conf import settings
from django.http import HttpResponse, Http404
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
I tweaked the code from #ans2human, and this worked for me in firefox and chrome:
def download_viewing_agreement_view(request):
file_path = pathlib.Path(settings.MEDIA_ROOT, 'filename.docx')
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
filename = 'filename.docx'
file_expr = "filename*=utf-8''{}".format(quote(filename)) # Handle a non-ASCII filename
response['Content-Disposition'] = 'attachment; {}'.format(file_expr)
return response
I am using django 1.11.2 and I want to have view with downloadable file. After click on link the file is downloading but file is empty and instead of png, I recieved empty txt file.
def download_file(request, uuid):
response = HttpResponse(content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(models.DownloadLink.objects.get(uuid=uuid).download_file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), models.DownloadLink.objects.get(uuid=uuid).download_file.file.url)
return response
EDIT:
value here response['Content-Length'] is http://127.0.0.1:8000/media/filer_public/49/54/4954a7bb-8ad3-4679-9248-bffc7d186ca8/photo-105221.jpeg
Just pass the file in HttpResponse constructor like this:
def download_file(request, uuid):
file = models.DownloadLink.objects.get(uuid=uuid).download_file
response = HttpResponse(file.file, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), file.file.url)
return response
From HttpResponse docs
content should be an iterator or a string. If it’s an iterator, it
should return strings, and those strings will be joined together to
form the content of the response. If it is not an iterator or a
string, it will be converted to a string when accessed.