docbow/docbow_project/docbow/upload_views.py

141 lines
5.3 KiB
Python

# -*- encoding: utf-8 -*-
import os
import json
from django.views.decorators.csrf import csrf_exempt
from django.urls import reverse
from django.core.files.storage import DefaultStorage
from django.core.files import File
from django.http import HttpResponse, Http404
from django.shortcuts import get_object_or_404
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from .models import FileTypeAttachedFileKind
from . import app_settings
def get_paths_for_id(upload_id):
storage = DefaultStorage()
path = os.path.join('upload', upload_id)
if not storage.exists(path):
return []
return list(storage.listdir(path)[1])
def get_files_for_id(upload_id):
storage = DefaultStorage()
path = os.path.join('upload', upload_id)
if not storage.exists(path):
return
for filepath in storage.listdir(path)[1]:
name = os.path.basename(filepath)
yield storage.open(os.path.join(path, name))
def get_data_for_id(upload_id):
for file_object in get_files_for_id(upload_id):
name = os.path.basename(file_object.name)
url = reverse('uploaded', args=[upload_id, name])
yield {'name': name, 'size': file_object.size, 'url': url, 'delete_url': url, 'delete_type': 'DELETE'}
def response_mimetype(request):
if "application/json" in request.META['HTTP_ACCEPT']:
return "application/json"
else:
return "text/plain"
class JSONResponse(HttpResponse):
"""JSON response class."""
def __init__(self, obj='', json_opts={}, mimetype="application/json", *args, **kwargs):
content = json.dumps(obj, **json_opts)
super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs)
@csrf_exempt
def upload(request, transaction_id, file_kind=None):
# do not store files in memory, use the disk
storage = DefaultStorage()
max_filename_length = 256
max_files = 0
try:
max_filename_length = int(request.GET.get('max_filename_length'))
except (ValueError, TypeError):
pass
try:
max_files = int(request.GET.get('cardinality'))
except (ValueError, TypeError):
pass
if file_kind:
file_kind = get_object_or_404(FileTypeAttachedFileKind, id=file_kind)
url = reverse('upload', kwargs={'transaction_id': transaction_id})
if request.method == 'POST' and request.FILES is not None:
data = []
for uploaded_file in request.FILES.values():
if uploaded_file.size > app_settings.MAX_FILE_SIZE:
message = _('File is too big, limit is %(max_file_size)s bytes')
message = message % {'max_file_size': app_settings.MAX_FILE_SIZE}
data.append({'name': uploaded_file.name, 'error': force_text(message)})
continue
if file_kind:
if not file_kind.match_file(uploaded_file):
message = _('invalid file type, check required ' 'file types for this field')
data.append({'name': uploaded_file.name, 'error': force_text(message)})
continue
uploaded_file.open()
if len(uploaded_file.name) > max_filename_length:
message = _('filename too long, only %d characters allowed') % max_filename_length
data.append({'name': uploaded_file.name, 'error': force_text(message)})
continue
if max_files:
count = len(get_paths_for_id(transaction_id))
if count >= max_files:
message = _('too much file attached, delete to add a new one')
data.append({'name': uploaded_file.name, 'error': force_text(message)})
continue
path = os.path.join('upload', str(transaction_id), uploaded_file.name)
filename = storage.save(path, uploaded_file)
url = '%s%s' % (url, os.path.basename(filename))
data.append(
{
'name': uploaded_file.name,
'size': uploaded_file.size,
'url': url,
'delete_url': url,
'delete_type': "DELETE",
}
)
response = JSONResponse(data, {}, response_mimetype(request))
response['Content-Disposition'] = 'inline; filename=files.json'
return response
else:
data = list(get_data_for_id(transaction_id))
response = JSONResponse(data, {}, response_mimetype(request))
response['Content-Disposition'] = 'inline; filename=files.json'
return response
def file_response(file_object):
if not isinstance(file_object, File):
file_object = File(file_object)
response = HttpResponse(file_object.chunks(), content_type='application/octet-stream')
response['Content-disposition'] = 'attachment'
return response
@csrf_exempt
def upload_file(request, transaction_id, filename):
storage = DefaultStorage()
path = os.path.join('upload', transaction_id, filename)
if request.method == 'DELETE':
storage.delete(path)
response = JSONResponse(True, {}, response_mimetype(request))
response['Content-Disposition'] = 'inline; filename=files.json'
return response
if not storage.exists(path):
raise Http404
return file_response(storage.open(path))