In my django app I am receiving a request with JSON like this:
{
"item1": false,
"item2": false,
"item3": "value",
"url": "http://downloadlink.example.net"
}
I have to save all data except url by which I just need to download text file and save it on server. How can I do the download part?
Code in view:
class Collect(View):
#csrf_exempt
def dispatch(self, request, *args, **kwargs):
return super(Collect, self).dispatch(request, *args, **kwargs)
def post(self, request):
data = json.loads(request.body.decode())
try:
item1 = data["item1"]
item2 = data["item2"]
item3 = data["item3"]
url = data["url"]
new_row = ModelName(
item1=item1,
item2=item2,
item3=item3,
)
new_row.save()
except Error:
return HttpResponse("Unable to save")
return HttpResponse("Saved")
So basically you can use urllib library and specifically its urlretrieve function to save the file in the temp folder locally first, then you can save it anywhere you want.
import urllib
response = urllib.urlretrieve(url)
contents = open(response[0]).read()
f = open('filename.ext','w')
f.write(contents)
f.close()
That is the basic example of how to save files from links. When you are saving files you should include the full path, so make sure to create a BASE_DIR constant in your settings file. You can go further and create a FileField or ImageField tables in your db, read here for more examples.
I think best practice solution is using celery because requesting another server while user is waiting is inappropriate and delays the user and sometimes it may take little long for bigger files. You can read the docs for more. I highly recommend it!
Related
I am calling the image upload API (Django REST API) from my view in a separate Django project.
My View
if request.method == 'POST' and request.FILES['file']:
try:
resp = requests.post(
"http://19.******/BankImage_API",
files = {"file" :request.FILES['file']},
headers={"content-type": "multipart/form-data",
"Authorization": "Token 71117971*************"
}, verify=False)
API
class Bankimageapi(APIView):
def post(self, request):
if request.method == 'POST' and request.FILES['file']:
try:
........
When I tried to upload an image, I got an error in API where FILES is <MultiValueDict: {}>:
django.utils.datastructures.MultiValueDictKeyError: 'file'
Please guide me to solve this problem.
In your view, the received request.FILES['file'] is an UploadedFile (see docs here), which is a subclass of File.
The requests library wants you to post the binary contents of the file. You can access the contents of the file using (file = request.FILES['file']):
read(): files={"file": file.read()}
file attribute: files = {"file": file.file} although I'm not sure this will give you control over whether or not this is binary.
Furthermore, you should remove the "content-type" header, which is added automatically by the requests package. This seems to mess up the body of your request.
What is the id of the input in the html?
Update:
The request.FILES is a dict that references the objects by their id on the html.
I'm trying to have my django rest framework app accept file uploads. The uploads should be accompanied by additional data that is descriptive of the file and is necessary for post-processing. Uploading the file seems to work fine, however, I can't seem to get the django app to access the other data. For example, I have the file more_info.html which I am trying to upload to my app:
import requests
url = "http://www.example.com/fileupload"
files = {'file':open('more_info.html','rb')
data = {'brand':'my brand','type':'html','level':'dev'}
headers = {'Content-type': 'multipart/form-data', 'Content-Disposition': 'attachment; filename="more_info.html"'}
r = requests.post(url,files=files,data=,headers=headers)
In my Django view I am trying to view my POST data with the following:
def post(self, request):
print(request.POST)
print(request.FILEs)
Both print statements are returning:
{u'file': <InMemoryUploadedFile: more_info.html (multipart/form-data)>}
How can I access the rest of the data in the request POST?
This line
r = requests.post(url,files=files,data=,headers=headers)
You seem to be missing assigning data to data=.
I have a text file in the static folder in my project root.
I'd like to serve it so I've created:
#csrf_exempt
def display_text(request):
content =
return HttpResponse(content, content_type='text/plain; charset=utf8')
How do I set the path to the textfile, or how do I read it in to 'content', so that I can display it.
Have a look at this question that lets Apache handle the file delivery with mod_xsendfile.
If you insist on having Django itself delivering the file, you could do the following:
from django.http import StreamingHttpResponse
#csrf_exempt
def display_text(request):
content = open('/your/file', 'r').read()
response = StreamingHttpResponse(content)
response['Content-Type'] = 'text/plain; charset=utf8'
return response
I need to send a pdf file and some other parameters in response to a get API call using django rest framework.
How can I do it?
I tried this but it gives an error <django.http.HttpResponse object at 0x7f7d32ffafd0> is not JSON serializable.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
short_report = open("somePdfFile", 'rb')
response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
return Response({'detail': 'this works',
'report': response})
The problem here is that you are trying to return a mix of JSON and PDF, which either isn't what you are looking for or is going to return a giant base64-encoded response. PDF is a binary format and JSON is a text format, and you can't really mix them well.
Within a DRF view you can directly return a Django response, which you already appear to be generating (the HttpResponse), and DRF will pass it through and skip the renderers. This is useful in cases like this, as it allows you to return a binary response such as an image or PDF without worrying about DRF's rendering layer causing problems.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
short_report = open("somePdfFile", 'rb')
response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
return response
The alternative is to encode the PDF as text, using something like base64 encoding. This will dramatically increase your response sizes, but it will allow you to use DRF's rendering layer without problems.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
import base64
short_report = open("somePdfFile", 'rb')
report_encoded = base64.b64encode(short_report.read())
return Response({'detail': 'this works',
'report': report_encoded})
But the route I would recommend here is to generate the PDF and store it in either your media storage, or an alternative private location, and provide a direct link to it in your response. This way you don't need to worry about the encoding issues, don't need to directly return the PDF, and don't need to worry about directly serving the PDF.
If you really need to serve binary files directly from your backend (e.g. when they're being generated dynamically) you can use a custom renderer:
from rest_framework.renderers import BaseRenderer
class BinaryFileRenderer(BaseRenderer):
media_type = 'application/octet-stream'
format = None
charset = None
render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data
Then use it in your action in a viewset:
from rest_framework.decorators import action
#action(detail=True, methods=['get'], renderer_classes=(BinaryFileRenderer,))
def download(self, request, *args, **kwargs):
with open('/path/to/file.pdf', 'rb') as report:
return Response(
report.read(),
headers={'Content-Disposition': 'attachment; filename="file.pdf"'},
content_type='application/pdf')
You can use DRF-PDF project with PDFFileResponse:
from rest_framework import status
from rest_framework.views import APIView
from drf_pdf.response import PDFFileResponse
from drf_pdf.renderer import PDFRenderer
class PDFHandler(APIView):
renderer_classes = (PDFRenderer, )
def get(self, request):
return PDFFileResponse(
file_path='/path/to/file.pdf',
status=status.HTTP_200_OK
)
But, maybe you cannot respond in both formats (json and stream).
You can send pdf file as a response to any request without installing any packages.
You can do the following.
def get(request, *args, **kwargs):
# some code
# some code
with fs.open("path/to/file/Report.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
filename = "Report.pdf"
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
return response
Now, open your postman and hit a get/post request.
Important: Before clicking send button in postman, select the option send and download.
With DRF you can write as following:
pdf_file_in_bytes = AnyModel.file.read() # file is a FileField
pdf_file_name = "PDF's Download Name"
response = Response(
headers={'Content-Disposition': f'attachment; filename={pdf_file_name}'},
content_type='application/pdf'
)
response.content = pdf_file_in_bytes
return response
Is it possible to show a PDF file in the Django view, rather than making the user have to download it to see it?
And if it is possible, how would it be done?
This is what I have so far -
#login_required
def resume(request, applicant_id):
#Get the applicant's resume
resume = File.objects.get(applicant=applicant_id)
fsock = open(resume.location, 'r')
response = HttpResponse(fsock, mimetype='application/pdf')
return response
Django has a class specifically for returning files, FileResponse. It streams files, so that you don't have to read the entire file into memory before returning it. Here you go:
from django.http import FileResponse, Http404
def pdf_view(request):
try:
return FileResponse(open('foobar.pdf', 'rb'), content_type='application/pdf')
except FileNotFoundError:
raise Http404()
If you have really large files or if you're doing this a lot, a better option would probably be to serve these files outside of Django using normal server configuration.
Simplistically, if you have a PDF file and you want to output it through a Django view, all you need to do is dump the file contents into the response and send it with the appropriate mimetype.
def pdf_view(request):
with open('/path/to/my/file.pdf', 'r') as pdf:
response = HttpResponse(pdf.read(), mimetype='application/pdf')
response['Content-Disposition'] = 'inline;filename=some_file.pdf'
return response
pdf.closed
You can probably just return the response directly without specifying Content-Disposition, but that better indicates your intention and also allows you specify the filename just in case the user decides to save it.
Also, note that the view above doesn't handle the scenario where the file cannot be opened or read for whatever reason. Since it's done with with, it won't raise any exceptions, but you still must return some sort of response. You could simply raise an Http404 or something, though.
PDF files must be opened as rb not r.
def pdf_view(request):
with open('/path / to /name.pdf', 'rb') as pdf:
response = HttpResponse(pdf.read(),content_type='application/pdf')
response['Content-Disposition'] = 'filename=some_file.pdf'
return response
Take out inline; if you want your file to be read from server. And also, the HttpResponse kwarg mimetype has been replaced by content_type:
(response['Content-Disposition'] = 'inline;filename=some_file.pdf')
def pdf_view(request):
with open('/app/../Test.pdf', 'r') as pdf:
response = HttpResponse(pdf.read(),content_type='application/pdf')
response['Content-Disposition'] = 'filename=some_file.pdf'
return response
pdf.closed
Following #radtek's answer above I decided to investigate a class-based view display. I tried to use View but it didn't have get_context_data() method.
I looked here for some guidance. I settled for BaseDetailView since I wanted to display just one object.
from django.http import FileResponse
from django.shortcuts import get_object_or_404
from django.views.generic.detail import BaseDetailView
class DisplayPdfView(BaseDetailView):
def get(self, request, *args, **kwargs):
objkey = self.kwargs.get('pk', None) #1
pdf = get_object_or_404(Pdf, pk=objkey) #2
fname = pdf.filename() #3
path = os.path.join(settings.MEDIA_ROOT, 'docs\\' + fname)#4
response = FileResponse(open(path, 'rb'), content_type="application/pdf")
response["Content-Disposition"] = "filename={}".format(fname)
return response
Commentary
1 This line accesses a named argument pk passed by the url calling the view.
2 This line gets the actual pdf model object.
3 I defined a method filename(self): return os.path.basename(self.file.name) in my model to help me get just the filename plus extension.
4 This line gets the complete filepath.
Then use file response as explained in the answers above. Also remember to use rb to read the pdf file
Here is a typical use-case for displaying a PDF using class-based views:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
class DisplayPDFView(View):
def get_context_data(self, **kwargs): # Exec 1st
context = {}
# context logic here
return context
def get(self, request, *args, **kwargs):
context = self.get_context_data()
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'inline; filename="worksheet_pdf.pdf"' # Can use attachment or inline
# pdf generation logic here
# open an existing pdf or generate one using i.e. reportlab
return response
# Remove login_required if view open to public
display_pdf_view = login_required(DisplayPDFView.as_view())
For generating your own pdf with reportlab see the Django project Docs on PDF Generation.
Chris Pratt's response shows a good example of opening existing PDFs.
Browsers aren't PDF readers (unless they have the proper plugin/addon).
You may want to render the PDF as HTML instead, which can be done from the backend or the frontend.
it worked for me
import re, os
import os
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def export_auto_doc(request):
name = request.GET.get('name', "")
filename = "path/to/file"+name+".pdf"
try:
if not re.search("^[a-zA-Z0-9]+$",name):
raise ValueError("Filename wrong format")
elif not os.path.isfile(filename):
raise ValueError("Filename doesn't exist")
else:
with open(filename, 'r') as pdf:
response = HttpResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'inline;filename='+name+'.pdf'
return response
pdf.closed
except ValueError as e:
HttpResponse(e.message)
The easiest way to do this is probably with an anchor in a template. For example, if you are using Django's templating engine (as most people who search for this probably are), simply serve it as a static file through an anchor.
In your template that will contain a link to the file, add at the very top
{% load static %}
Then, wherever you want to link to your pdf, put
Click me
The first line tells Django to look in the directories configured for static files in settings.py. The path that you use in the anchor tag is relative to any of the directories that you configured as static directories in settings.py. When you click the rendered link, it should display the PDF in your browser, provided you have your static files pathed correctly.
I am just throwing this out there.
You can simply add your PDF resume to your static files.
If you are using White Noise to serve your static files, then you don't even need to make the view. Just then access your resume at the static location.
I added mine, here it is: TIm-D_Nice.pdf
Warning: This doesn't solve the login_required requirement in the question
Use iframe url=url of pdf tag and give url of that pdf and make sure that your user will have full control of the project then pdf will be displayed on web screen
def pdf_view(request,pdfSlug):
a = Pdf.objects.get(pdf_slug=pdfSlug)
with open(str(a.pdf_file.path), 'rb') as pdf:
response = FileResponse(pdf.read(), content_type='application/pdf')
response['Content-Disposition'] = 'filename=a.pdf'
return response
pdf.closed
look this i'ts warked for me