python script to handle uploaded file via http post - python

I am working on a django project where i try to upload a file via http post request.
my upload script is :
url=r'http://MYSITEURL:8000/upload'
files={'file':open('1.png','rb')}
r=requests.post(url,files=files)
my receiving side is in my django site , in views.py:
def upload_image(request):
from py_utils import open_py_shell;open_py_shell.open_py_shell()
when i do request.FILES i can see all the post details,
what i want to know is how to save it in the server side once i got the post request

What you have in request.FILES is InMemoryUploadedFile. You just need to save it somewhere in file system.
This is example method taken from Django docs:
def handle_uploaded_file(f):
with open('some/file/name.txt', 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)

I think you can work with models well. It will be the right way for Django. Here is an example, models.py file:
from django.db import models
from django.conf import settings
import os
import hashlib
def instanced_file(instance, filename):
splitted_name = filename.split('.')
extension = splitted_name[-1]
return os.path.join('files', hashlib.md5(str(instance.id).encode('UTF-8')).hexdigest() + '.' + extension)
class File(models.Model):
name = models.FileField('File', upload_to = instanced_file)
def get_file_url(self):
return '%s%s' % (settings.MEDIA_URL, self.name)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
After the creating models create forms and go on.

Related

Saving PDFs to disk as they are generated with django-wkhtmltopdf

What I'm trying to implement is this:
User sends query parameters from React FE microservice to the Django BE microservice.
URI is something like /api/reports?startingPage=12&dataView=Region
These PDFs are way too big to be generated in FE, so doing it server side
Request makes its way into the view.py where the data related to dataView=Region is queried from the database, each row is iterated through and a PDF report is generated for each item
Each dataView=Region can consist of a few hundred items and each of those items is its own report that can be a page long or several pages long
As the reports are generated, they should be saved to the server persistent volume claim and not be sent back to FE until they have all run.
When they have all run, I plan to use pypdf2 to combine all of the PDFs into one large file.
At that point, the file is sent back to the FE to download.
I'm only working on 1. and 3. at this point and I'm unable to:
Get the files to save to storage
Prevent the default behavior of the PDF being sent back to the FE after it has been generated
The PDFs are being generated, so that is good.
I'm trying to implement the suggestions as found here, but I'm not getting the desired results:
Save pdf from django-wkhtmltopdf to server (instead of returning as a response)
This is what I currently have on the Django side:
# urls.py
from django.urls import path
from .views import GeneratePDFView
app_name = 'Reports'
urlpatterns = [
path('/api/reports',
GeneratePDFView.as_view(), name='generate_pdf'),
]
# views.py
from django.conf import settings
from django.views.generic.base import TemplateView
from rest_framework.permissions import IsAuthenticated
from wkhtmltopdf.views import PDFTemplateResponse
# Create your views here.
class GeneratePDFView(TemplateView):
permission_classes = [IsAuthenticated]
template_name = 'test.html'
filename = 'test.pdf'
def generate_pdf(self, request, **kwargs):
context = {'key': 'value'}
# generate response
response = PDFTemplateResponse(
request=self.request,
template=self.template_name,
filename=self.filename,
context=context,
cmd_options={'load-error-handling': 'ignore'})
self.save_pdf(response.rendered_content, self.filename)
# Handle saving the document
# This is what I'm using elsewhere where files are saved and it works there
def save_pdf(self, file, filename):
with open(settings.PDF_DIR + '/' + filename, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
# settings.py
...
DOWNLOAD_ROOT = '/mnt/files/client-downloads/'
MEDIA_ROOT = '/mnt/files/client-submissions/'
PDF_DIR = '/mnt/files/pdf-sections/'
...
I should note the other DOWNLOAD_ROOT and MEDIA_ROOT are working fine where the app uses them. I've even tried using settings.MEDIA_ROOT because I know it works, but still nothing is saved there. But as you can see, I'm starting out super basic and haven't added a query, loops, etc.
My save_pdf() is different than the SO question I linked to because that is what I'm using in other parts of my application and it is saving files fine there. I did try what they provided in the SO question, but had the same results with it not saving. That being:
with open("file.pdf", "wb") as f:
f.write(response.rendered_content)
So what do I need to do to get these PDFs to save to disk?
Perhaps I need to be using a different library for my needs as django-wkhtmltopdf seems to do a number of things out of the box that I don't want that I'm not clear I can override.
OK, my smooth brain gained a few ripples overnight and figured it out this morning:
# views.py
class GeneratePDFView(TemplateView):
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
template_name = 'test.html'
filename = 'test.pdf'
context = {'key': 'value'}
# generate response
response = PDFTemplateResponse(
request=request,
template=template_name,
filename=filename,
context=context,
cmd_options={'load-error-handling': 'ignore'})
# write the rendered content to a file
with open(settings.PDF_DIR + '/' + filename, "wb") as f:
f.write(response.rendered_content)
return HttpResponse('Hello, World!')
This saved the PDF to disk and also did not respond with the PDF. Obviously a minimally functioning example that I can expand on, but at least got those two issues figured out.

How to use rest api in kivy to upload a file to a django website

I am currently working on a system that involves building a website an a mobile application. I am using kivy to build my application however am facing an issue with using the django rest api to upload files to the site through kivy. How can I go about it.
This is my function in main.py for uploading the file
def upload(self, filepass, filename):
print(str(filename))
try:
requests.post('http://127.0.0.1:8000/upload/', filepass)
except:
toast('Could not upload file')
This is my api view in my views.py
class FileUploadView(APIView):
parser_class = (FileUploadParser,)
def post(self, request, *args, **kwargs):
file_serializer = FileSerializer(data=request.data['files'])
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
This is my models.py for the database of the uploaded file
class File(models.Model):
file = models.ImageField(upload_to='landbroker/', default='default.png')
def __str__(self):
return self.file.name
This is my serializers.py for the file upload
from rest_framework import serializers
from .models import File
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
fields = "__all__"
And finally my urls.py
path('upload/', views.FileUploadView.as_view())
With all that, whenever I try to submit the image django outputs unsupported file format. Please help.
I have spent a full year with no answer. But I now think that if someone just uses get in the django site without verifying the form of the sent media it would actually work.

How to unzip a zip file and show it as list in python-Django

I'am learning python and django, I want to make an endpoint that takes zip file and iterate through it and shows me the item list inside the zip file. What would be the easiest way to implement this? I have tried something, as my knowledge in django is not good at all.
from django.core.files.storage import FileSystemStorage
import zipfile
class Upload(View):
def post(self, request):
context = {}
if request.method == 'POST':
uploaded_file = request.FILES['filename']
zf = zipfile.ZipFile(uploaded_file)
print zf.namelist()
for filename in zf.namelist():
fs = FileSystemStorage()
rename = 'uploadedFile.jpg'
name = fs.save(rename, filename)
context['url'] = fs.url(name)
return render(request, 'app.html', context)
so basically my purpose is to take a zipfile from and rename it and makke an url for each. My stated code is not the right way to do this as its bringing some error, would you please help me with the right way?
I haven't tested this code on live django project, but this is the way I'd code your task.
from zipfile import ZipFile
def get_filenames(path_to_zip):
""" return list of filenames inside of the zip folder"""
with ZipFile(path_to_zip, 'r') as zip:
return zip.namelist()
and in your view:
class Upload(View):
def post(self, request):
# as you use class based views you can skip the check of request type (request.method == 'POST').
# It always will be POST as it post() method
filenames = get_filenames('your_zip.zip')
return render(request, 'app.html', {'filenames': filenames})
It's just example, but you can add some more extra logic, like renaming etc.
P.S. Can't add a comment, but if you are on python3 and got SyntaxError on
print zf.namelist()
just wrap it in brackets:
print(zf.namelist())

Using Django Rest Framework as security layer for file system processing

I'm trying to protect my remote server file system from unauthorized users.
I have a remote storage on a different server that store and process PDF's and PNG's from all kind of processes.
I'm using Python 2.7 with Django 1.8 and Django Rest Framework.
I trying to implement very basic, "Proxy Layer" that will give my control on who ever use file system.
This is my view.py:
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
import requests
class Reports(APIView):
permission_classes = (permissions.AllowAny,) #Thats only for now...
def get(self, request, ssn, validity, file):
response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
return Response(response)
This concept works for any other GET POST PUT DELETE request that is text based response (For example json response from the remote server).
My problem is when I call this view, I get in the browser the default REST method definition page.
This is the response screenshot
As #AlexMorozov said in his comment, you should revert back to HttpResponse.
You can see by exploring this Django Snippet.
Here is the code as I see it:
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
from django.http import HttpResponse
import requests
import mimetypes
class Reports(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, ssn, validity, file):
response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
mimetype = mimetypes.guess_type(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file) #Return an array
return HttpResponse(response, content_type=mimetype[0])
Give it a go. Good luck :)
The problem is you can't simply wrap a requests response with a DRF Response and get it done. Change your code to:
from django.core.servers.basehttp import FileWrapper
upstream_response = requests.get(settings.PROXY_BASE_URL + "/reports/" + ssn + "/" + validity + "/" + file)
response = Response(FileWrapper(upstream_response.content), content_type='YOUR_MIME_TYPE')
response['Content-Disposition'] = 'attachment; filename="%s"' % 'your_filename.ext'
return response
BTW, there are other (more clean, IMO) ways to solve this particular task. Take a look at django-sendfile app. It offloads the actual file serving to a nginx or the server of your choice, while allowing you to check permissions and stuff.

protecting user uploaded files django

How can I allow users to upload files to their own, user designated folder, and only see files that they have uploaded? I am using django file-transfer. Currently it gives me a choice of what file to put the media in, but I can put it in any user's file and view every user's media. Here is my uploads/models.py:
from django.db import models
from django.contrib.auth.models import User, UserManager
def uploadmodel_file_upload_to(instance, filename):
print 'instance.user.username = '+ str(instance.user.username)
return 'uploads/%s/%s' % (instance.user.username, filename)
class UploadModel(models.Model):
user = models.ForeignKey('auth.user')
file = models.FileField(upload_to=uploadmodel_file_upload_to)
uploadmodel_file_upload_to returns a relative path. To build the full path, django prepends settings.MEDIA_ROOT. MEDIA_ROOT is supposed to be public readable.
So we want to save the file outside MEDIA_ROOT. Add something like this to settings.py:
import os.path
PROJECT_ROOT=os.path.abspath(os.path.dirname(__file__))
PROTECTED_MEDIA_ROOT=os.path.join(PROJECT_ROOT, 'protected_uploads')
Now you can update uploadmodel_file_upload_to to return an absolute path:
def uploadmodel_file_upload_to(instance, filename):
return '%s/%s/%s' % (settings.PROTECTED_MEDIA_ROOT, instance.user.username,
filename)
Now that the files are saved in /project/path/protected_uploads, we need to add a view to serve it, for example:
import os
import mimetypes
from django import shortcuts
from django import http
from django.conf import settings
from django.views.static import was_modified_since
from django.utils.http import http_date
from .models import *
def serve_upload(request, upload_id):
upload = shortcuts.get_object_or_404(UploadModel, pk=upload_id)
fullpath = upload.file.path
if request.user != upload.user:
return http.HttpResponseForbidden()
statobj = os.stat(fullpath)
mimetype, encoding = mimetypes.guess_type(fullpath)
mimetype = mimetype or 'application/octet-stream'
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj.st_mtime, statobj.st_size):
return http.HttpResponseNotModified(mimetype=mimetype)
response = http.HttpResponse(open(fullpath, 'rb').read(), mimetype=mimetype)
response["Last-Modified"] = http_date(statobj.st_mtime)
response["Content-Length"] = statobj.st_size
if encoding:
response["Content-Encoding"] = encoding
return response
And a URL:
url(r'serve_upload/(?P<upload_id>\d+)/$', 'serve_upload'),

Categories