141 lines
5.3 KiB
Python
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))
|