Django 和 Dropzone.js
Django and Dropzone.js
当我使用 dropzone 上传文件时,它会将它们添加到数据库中,但它们没有文件,只有 ID 和创建日期。我认为观点是问题所在,但我已经尝试了很多东西,但我无法弄清楚。请参阅下面我的编辑以获取更详细的说明。
这是风景
@login_required(login_url='/dashboard-login/')
def dashboard(request):
current_user = request.user
current_client = request.user.client
files = ClientUpload.objects.filter(client=current_client)
form = UploadFileForm()
if request.method == 'POST':
if request.FILES is None:
logger = logging.getLogger(__name__)
logger.warning("No files were attached to the upload.")
return HttpResponseBadRequest('No Files Attached.')
if form.is_valid():
upload = form.save()
form = UploadFileForm(request.POST, request.FILES)
else:
uploaded_files = [request.FILES.get('file_upload[%d]' % i)
for i in range(0, len(request.FILES))]
for f in uploaded_files:
client_upload = ClientUpload.objects.create(client=current_client, file_upload=f)
#for key in request.FILES:
# cupload = ClientUpload.objects.create(client=current_client, file_upload=request.FILES[key])
logger = logging.getLogger(__name__)
logger.debug(request.FILES)
logger.info("File(s) uploaded from " + current_client.company)
return HttpResponseRedirect(reverse('dashboard'))
data = {'form': form, 'client': current_client, 'files': files}
return render_to_response('dashboard.html', data, context_instance=RequestContext(request))
这是我的 dz 选项:
url: '127.0.0.1:8003/dashboard/',
method: "post",
withCredentials: false,
parallelUploads: 12,
uploadMultiple: true,
maxFilesize: 256*4*2,
paramName: "file_upload",
createImageThumbnails: true,
maxThumbnailFilesize: 20,
thumbnailWidth: 100,
thumbnailHeight: 100,
maxFiles: 12,
params: {},
clickable: true,
ignoreHiddenFiles: true,
acceptedFiles: null,
acceptedMimeTypes: null,
autoProcessQueue: false,
addRemoveLinks: true,
previewsContainer: null,
dictDefaultMessage: "Drop files here to upload",
dictFallbackMessage: "Your browser does not support drag and drop file uploads.",
dictFallbackText: "Please use the fallback form below to upload your files.",
dictFileTooBig: "File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.",
dictInvalidFileType: "You can't upload files of this type.",
dictResponseError: "Server responded with {{statusCode}} code.",
dictCancelUpload: "Cancel upload",
dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
dictRemoveFile: "Remove",
dictRemoveFileConfirmation: null,
dictMaxFilesExceeded: "You can only upload {{maxFiles}} files.",
这是模板:
{% load i18n %}
{% load staticfiles %}
{% load crispy_forms_tags %}
<link href="{% static 'css/dropzone2.css' %}" type="text/css" rel="stylesheet"/>
<form class="dropzone" id="myDropzone" method="post" action="{% url 'dashboard' %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" multiple />
</div>
</form>
<button class="upload-control btn-success btn" type="submit" id='submit-all' onclick="document.getElementById('myDropzone').submit()">
<i class="glyphicon glyphicon-upload"></i>
<span>{% trans 'Submit' %}</span>
</button>
<style>
.upload-control {
margin-top: 10px;
margin-bottom: 0px;
}
</style>
<script src="{% static 'js/dropzone.js' %}"></script>
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script type="text/javascript">
Dropzone.autoDiscover = false
$(document).ready(function() {
Dropzone.options.myDropzone = {
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function(e) {
e.stopPropagation();
e.preventDefault();
myDropzone.processQueue();
});
this.on("sendingmultiple", function() {
// Figure out what I want here or if I want at all
});
this.on("successmultiple", function(files, response) {
window.location.reload();
});
this.on("errormultiple", function(files, response) {
// Figure out what I want here or if I want at all
});
}
// Do I need this?
//myDropzone.on('success', myDropzone.processQueue.bind(myDropzone));
};
});
</script>
编辑:
将 http:// 添加到 url 设置后现在可以使用了。但是当我上传一个文件时,它被添加到数据库中,但是文件字段是空白的。 multivaluedict 在我打印出来时显示文件,但是当它保存到数据库时,文件字段中没有任何内容。
当我上传一个文件时,我在 request.FILES:
中得到了这个
<MultiValueDict: {u'file_upload[]': [<InMemoryUploadedFile: normal.PNG (image/png)>]}>
当我上传两个时,我在 request.FILES 中得到了这个:
<MultiValueDict: {u'file_upload[]': [<TemporaryUploadedFile: normal.PNG (image/png)>]}>
尽管是两个文件,但它只显示一个,但将它们都添加到数据库中(都没有文件,只有 ID 和创建日期)。还有什么是 TemporaryUploadedFile 和 InMemoryUploadedFile?
当我上传多个但它没有时,它应该在 u'file_upload[]' 中有索引。我的设置正确,可以上传多个。
但我似乎无法将它们从 MultiValueDict 中删除。当我尝试类似的东西时:
for upload in request.FILES:
client_upload = ClientUpload.objects.create(client=current_client, file_upload=upload)
我 运行 遇到管理面板显示 ID 和时间但没有文件的问题。上传一个或多个时会发生这种情况。我也不确定 InMemoryUploadedfile 和 TemporaryUploadedFile 之间有什么区别。如何从 MultiValueDict 中提取文件? get() 不起作用,使用列表 comp 我只得到一个空列表。
另一件奇怪的事情是,当我上传某些文件时,MultiValueDict 是空的,而其他文件则不是。此外,似乎我的视图被调用不止一次(根据日志输出),这是正常的,除了它应该是一个 post 然后重定向到一个 get,但它似乎有不止一个 post 请求。我检查了 chrome 中的开发工具,但我只看到一个,但奇怪的是,每次提交时它都会输出我的日志语句两次。我知道这个问题可能在我看来,但我已经尝试了很多东西,但无法找出问题所在。
有人有什么想法吗?
在你的 Javascript 中,我认为你想将其用于 paramName:
paramName: "file_upload",
为了让 Django Form 或 ModelForm 识别上传的文件。
还要确保上传请求使用的是 multipart/form-data 内容类型。
此外,试试这个而不是 "get_list":
dz_files = request.FILES.getlist("file_upload")
我自己正在使用 Dropzone 和 Django 为每个上传的文件创建 Image 对象,这似乎类似于您想要做的。我想指出一些我经历过的事情,并向您展示我是如何做的,看看是否有帮助。
你需要什么
为了在数据库中为使用 Dropzone 上传的文件创建记录,您需要做的事情是:
- Dropzone HTML 表格
- Dropzone的Javascript初始化。
- 处理上传文件的 Django 视图。
我不明白你在用表格做什么(它只是验证吗?)但它似乎是不必要的。您不需要(也不要使用它)来实际保存文件。
正在访问上传的文件
首先让我们谈谈如何访问request.FILES
中的文件。通过在您的 Dropzone 配置上设置 uploadMultiple: true
,您可以让 Dropzone 不发送 dzfile
,而是发送表示为 dzfile[%d]
的每个文件(即 dzfile[0]
、dzfile[1]
等) .
即使情况并非如此,您使用 request.FILES 就像它是一个列表 (for f in request.FILES
),但就像您指出的那样,它实际上是一个字典。
这是我打印 request.FILES
时 Python 显示的内容:
<MultiValueDict: {u'dzfile[1]': [<InMemoryUploadedFile: image2.jpg (image/jpeg)>], u'dzfile[2]': [<InMemoryUploadedFile: image3.jpg (image/jpeg)>], u'dzfile[0]': [<InMemoryUploadedFile: image1.jpg (image/jpeg)>]}>
要访问实际文件,您需要 get
每个密钥的名称。
files = [request.FILES.get('dzfile[%d]' % i)
for i in range(0, len(request.FILES))]
现在您有了想要的文件列表。只需遍历它并根据需要创建对象。我不确定你的模型是如何工作的,所以我将进行近似。
for f in files:
# Create a ClientUpload object by setting its FK to client and
# FileField to the file. Correct me if I deduced the models incorrectly
client_upload = ClientUpload.objects.create(
client=current_client,
file_upload=f,
)
这应该足以创建您想要的对象。
空降区Javascript
看来在你添加到提交按钮的Click事件监听器中你必须添加
e.preventDefault();
e.stopPropagation();
在调用 processQueue() 之前避免双重表单提交。
至于sendingmultiple
、successmultiple
和errormultiple
,你想在那里发生什么?注释仅用于指示何时触发这些事件。
我个人使用:
this.on('sendingmultiple', function () {
// `sendingmultiple` to hide the submit button
$('#my-dropzone').find('button[type=submit]').hide();
});
this.on('successmultiple', function (files, response) {
// `successmultiple` to reload the page (and show the updated info)
window.location.reload();
});
this.on('errormultiple', function (files, response) {
// `errormultiple` to un-hide the button
$('#my-dropzone').find('button[type=submit]').show();
});
不过你当然可以随心所欲。
最后,您打算如何处理 <script>
标记中的最后一行?我不是很明白,看起来如果你想在成功时重新处理队列。它似乎不属于那里。
如果有任何问题请评论,但此设置对我来说工作正常。
我不知道我现在展示的内容是否会特别帮助你,但也许它会帮助其他人,使用 dropzone 让我做了一个解决方法,因为 ajax,文件和 django 结合起来总是有点复杂.
所以在我的 html 中我有这个代码:
<div class="logos">
<i class="fa fa-upload" id="dropzone_icon" data-name="icon" title="{% trans "Drag and drop or click" %}" alt="{% trans "Drag and drop or click" %}" ></i>
<input type="hidden" name="icon" value="" >
<input type="hidden" name="icon_name" value="" >
<div class="img-holder">
<img title='{% trans "Upload a Company Icon" %}' id="img_icon" alt='{% trans "Company icon" %}' src="{* icon *}"/>
</div>
<label>{% trans "Company Icon" %}</label>
</div>
在我的 js 中我得到了这个:
dropz = new Dropzone(value, {
url: "branding/dropzone",
maxFiles: 1,
acceptedFiles: "image/*",
thumbnail: function(file, dataUrl) {
/* change existing image */
var file_type = file.name.split('.');
file_type = file_type[file_type.length - 1];
if(!(file_type=="png" || file_type=="jpg" || file_type=="jpeg")){
createAlert('file type must be .png, .jpg or .jpeg', '#pp_content', 'alert');
return false;
}
$("input[name='icon']").val(dataUrl.split(",")[1]);
$("input[name='icon_name']").val(file.name);
$("#img_" + type).prop("src", dataUrl);
this.removeFile(file);
},
previewTemplate: "<span></span>",
autoProcessQueue: false
});
这告诉 dropzone 将值插入输入(图像的 base64 表示和文件名)所以基本上我将图像作为字符串发送。
在 ajax 中以表单形式发送输入后,这就是我在 views.py 中处理它们的方式:
import datetime
from django.core.files.base import ContentFile
def base64_to_image(img_b64,img_name):
"""
Creates image file from bas64 encoded data
:param img_b64: base64 data
:param img_name: filename for created image
:return: file or false if there's no data
"""
if img_b64:
image_data = b64decode(img_b64)
img_file = ContentFile(image_data,datetime.datetime.now().strftime("%y%d%m_%H%M%S") + img_name)
return img_file
else:
return False
company.icon = base64_to_image(request.POST["icon"], request.POST["icon_name"])
company.save()
这是我在使用 dropzone 时所做的工作,也许它也会对这里的其他人有所帮助
当我使用 dropzone 上传文件时,它会将它们添加到数据库中,但它们没有文件,只有 ID 和创建日期。我认为观点是问题所在,但我已经尝试了很多东西,但我无法弄清楚。请参阅下面我的编辑以获取更详细的说明。
这是风景
@login_required(login_url='/dashboard-login/')
def dashboard(request):
current_user = request.user
current_client = request.user.client
files = ClientUpload.objects.filter(client=current_client)
form = UploadFileForm()
if request.method == 'POST':
if request.FILES is None:
logger = logging.getLogger(__name__)
logger.warning("No files were attached to the upload.")
return HttpResponseBadRequest('No Files Attached.')
if form.is_valid():
upload = form.save()
form = UploadFileForm(request.POST, request.FILES)
else:
uploaded_files = [request.FILES.get('file_upload[%d]' % i)
for i in range(0, len(request.FILES))]
for f in uploaded_files:
client_upload = ClientUpload.objects.create(client=current_client, file_upload=f)
#for key in request.FILES:
# cupload = ClientUpload.objects.create(client=current_client, file_upload=request.FILES[key])
logger = logging.getLogger(__name__)
logger.debug(request.FILES)
logger.info("File(s) uploaded from " + current_client.company)
return HttpResponseRedirect(reverse('dashboard'))
data = {'form': form, 'client': current_client, 'files': files}
return render_to_response('dashboard.html', data, context_instance=RequestContext(request))
这是我的 dz 选项:
url: '127.0.0.1:8003/dashboard/',
method: "post",
withCredentials: false,
parallelUploads: 12,
uploadMultiple: true,
maxFilesize: 256*4*2,
paramName: "file_upload",
createImageThumbnails: true,
maxThumbnailFilesize: 20,
thumbnailWidth: 100,
thumbnailHeight: 100,
maxFiles: 12,
params: {},
clickable: true,
ignoreHiddenFiles: true,
acceptedFiles: null,
acceptedMimeTypes: null,
autoProcessQueue: false,
addRemoveLinks: true,
previewsContainer: null,
dictDefaultMessage: "Drop files here to upload",
dictFallbackMessage: "Your browser does not support drag and drop file uploads.",
dictFallbackText: "Please use the fallback form below to upload your files.",
dictFileTooBig: "File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.",
dictInvalidFileType: "You can't upload files of this type.",
dictResponseError: "Server responded with {{statusCode}} code.",
dictCancelUpload: "Cancel upload",
dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
dictRemoveFile: "Remove",
dictRemoveFileConfirmation: null,
dictMaxFilesExceeded: "You can only upload {{maxFiles}} files.",
这是模板:
{% load i18n %}
{% load staticfiles %}
{% load crispy_forms_tags %}
<link href="{% static 'css/dropzone2.css' %}" type="text/css" rel="stylesheet"/>
<form class="dropzone" id="myDropzone" method="post" action="{% url 'dashboard' %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="fallback">
<input name="file" type="file" multiple />
</div>
</form>
<button class="upload-control btn-success btn" type="submit" id='submit-all' onclick="document.getElementById('myDropzone').submit()">
<i class="glyphicon glyphicon-upload"></i>
<span>{% trans 'Submit' %}</span>
</button>
<style>
.upload-control {
margin-top: 10px;
margin-bottom: 0px;
}
</style>
<script src="{% static 'js/dropzone.js' %}"></script>
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script type="text/javascript">
Dropzone.autoDiscover = false
$(document).ready(function() {
Dropzone.options.myDropzone = {
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function(e) {
e.stopPropagation();
e.preventDefault();
myDropzone.processQueue();
});
this.on("sendingmultiple", function() {
// Figure out what I want here or if I want at all
});
this.on("successmultiple", function(files, response) {
window.location.reload();
});
this.on("errormultiple", function(files, response) {
// Figure out what I want here or if I want at all
});
}
// Do I need this?
//myDropzone.on('success', myDropzone.processQueue.bind(myDropzone));
};
});
</script>
编辑:
将 http:// 添加到 url 设置后现在可以使用了。但是当我上传一个文件时,它被添加到数据库中,但是文件字段是空白的。 multivaluedict 在我打印出来时显示文件,但是当它保存到数据库时,文件字段中没有任何内容。
当我上传一个文件时,我在 request.FILES:
中得到了这个<MultiValueDict: {u'file_upload[]': [<InMemoryUploadedFile: normal.PNG (image/png)>]}>
当我上传两个时,我在 request.FILES 中得到了这个:
<MultiValueDict: {u'file_upload[]': [<TemporaryUploadedFile: normal.PNG (image/png)>]}>
尽管是两个文件,但它只显示一个,但将它们都添加到数据库中(都没有文件,只有 ID 和创建日期)。还有什么是 TemporaryUploadedFile 和 InMemoryUploadedFile?
当我上传多个但它没有时,它应该在 u'file_upload[]' 中有索引。我的设置正确,可以上传多个。
但我似乎无法将它们从 MultiValueDict 中删除。当我尝试类似的东西时:
for upload in request.FILES:
client_upload = ClientUpload.objects.create(client=current_client, file_upload=upload)
我 运行 遇到管理面板显示 ID 和时间但没有文件的问题。上传一个或多个时会发生这种情况。我也不确定 InMemoryUploadedfile 和 TemporaryUploadedFile 之间有什么区别。如何从 MultiValueDict 中提取文件? get() 不起作用,使用列表 comp 我只得到一个空列表。
另一件奇怪的事情是,当我上传某些文件时,MultiValueDict 是空的,而其他文件则不是。此外,似乎我的视图被调用不止一次(根据日志输出),这是正常的,除了它应该是一个 post 然后重定向到一个 get,但它似乎有不止一个 post 请求。我检查了 chrome 中的开发工具,但我只看到一个,但奇怪的是,每次提交时它都会输出我的日志语句两次。我知道这个问题可能在我看来,但我已经尝试了很多东西,但无法找出问题所在。
有人有什么想法吗?
在你的 Javascript 中,我认为你想将其用于 paramName:
paramName: "file_upload",
为了让 Django Form 或 ModelForm 识别上传的文件。
还要确保上传请求使用的是 multipart/form-data 内容类型。
此外,试试这个而不是 "get_list":
dz_files = request.FILES.getlist("file_upload")
我自己正在使用 Dropzone 和 Django 为每个上传的文件创建 Image 对象,这似乎类似于您想要做的。我想指出一些我经历过的事情,并向您展示我是如何做的,看看是否有帮助。
你需要什么
为了在数据库中为使用 Dropzone 上传的文件创建记录,您需要做的事情是:
- Dropzone HTML 表格
- Dropzone的Javascript初始化。
- 处理上传文件的 Django 视图。
我不明白你在用表格做什么(它只是验证吗?)但它似乎是不必要的。您不需要(也不要使用它)来实际保存文件。
正在访问上传的文件
首先让我们谈谈如何访问request.FILES
中的文件。通过在您的 Dropzone 配置上设置 uploadMultiple: true
,您可以让 Dropzone 不发送 dzfile
,而是发送表示为 dzfile[%d]
的每个文件(即 dzfile[0]
、dzfile[1]
等) .
即使情况并非如此,您使用 request.FILES 就像它是一个列表 (for f in request.FILES
),但就像您指出的那样,它实际上是一个字典。
这是我打印 request.FILES
时 Python 显示的内容:
<MultiValueDict: {u'dzfile[1]': [<InMemoryUploadedFile: image2.jpg (image/jpeg)>], u'dzfile[2]': [<InMemoryUploadedFile: image3.jpg (image/jpeg)>], u'dzfile[0]': [<InMemoryUploadedFile: image1.jpg (image/jpeg)>]}>
要访问实际文件,您需要 get
每个密钥的名称。
files = [request.FILES.get('dzfile[%d]' % i)
for i in range(0, len(request.FILES))]
现在您有了想要的文件列表。只需遍历它并根据需要创建对象。我不确定你的模型是如何工作的,所以我将进行近似。
for f in files:
# Create a ClientUpload object by setting its FK to client and
# FileField to the file. Correct me if I deduced the models incorrectly
client_upload = ClientUpload.objects.create(
client=current_client,
file_upload=f,
)
这应该足以创建您想要的对象。
空降区Javascript
看来在你添加到提交按钮的Click事件监听器中你必须添加
e.preventDefault();
e.stopPropagation();
在调用 processQueue() 之前避免双重表单提交。
至于sendingmultiple
、successmultiple
和errormultiple
,你想在那里发生什么?注释仅用于指示何时触发这些事件。
我个人使用:
this.on('sendingmultiple', function () {
// `sendingmultiple` to hide the submit button
$('#my-dropzone').find('button[type=submit]').hide();
});
this.on('successmultiple', function (files, response) {
// `successmultiple` to reload the page (and show the updated info)
window.location.reload();
});
this.on('errormultiple', function (files, response) {
// `errormultiple` to un-hide the button
$('#my-dropzone').find('button[type=submit]').show();
});
不过你当然可以随心所欲。
最后,您打算如何处理 <script>
标记中的最后一行?我不是很明白,看起来如果你想在成功时重新处理队列。它似乎不属于那里。
如果有任何问题请评论,但此设置对我来说工作正常。
我不知道我现在展示的内容是否会特别帮助你,但也许它会帮助其他人,使用 dropzone 让我做了一个解决方法,因为 ajax,文件和 django 结合起来总是有点复杂.
所以在我的 html 中我有这个代码:
<div class="logos">
<i class="fa fa-upload" id="dropzone_icon" data-name="icon" title="{% trans "Drag and drop or click" %}" alt="{% trans "Drag and drop or click" %}" ></i>
<input type="hidden" name="icon" value="" >
<input type="hidden" name="icon_name" value="" >
<div class="img-holder">
<img title='{% trans "Upload a Company Icon" %}' id="img_icon" alt='{% trans "Company icon" %}' src="{* icon *}"/>
</div>
<label>{% trans "Company Icon" %}</label>
</div>
在我的 js 中我得到了这个:
dropz = new Dropzone(value, {
url: "branding/dropzone",
maxFiles: 1,
acceptedFiles: "image/*",
thumbnail: function(file, dataUrl) {
/* change existing image */
var file_type = file.name.split('.');
file_type = file_type[file_type.length - 1];
if(!(file_type=="png" || file_type=="jpg" || file_type=="jpeg")){
createAlert('file type must be .png, .jpg or .jpeg', '#pp_content', 'alert');
return false;
}
$("input[name='icon']").val(dataUrl.split(",")[1]);
$("input[name='icon_name']").val(file.name);
$("#img_" + type).prop("src", dataUrl);
this.removeFile(file);
},
previewTemplate: "<span></span>",
autoProcessQueue: false
});
这告诉 dropzone 将值插入输入(图像的 base64 表示和文件名)所以基本上我将图像作为字符串发送。
在 ajax 中以表单形式发送输入后,这就是我在 views.py 中处理它们的方式:
import datetime
from django.core.files.base import ContentFile
def base64_to_image(img_b64,img_name):
"""
Creates image file from bas64 encoded data
:param img_b64: base64 data
:param img_name: filename for created image
:return: file or false if there's no data
"""
if img_b64:
image_data = b64decode(img_b64)
img_file = ContentFile(image_data,datetime.datetime.now().strftime("%y%d%m_%H%M%S") + img_name)
return img_file
else:
return False
company.icon = base64_to_image(request.POST["icon"], request.POST["icon_name"])
company.save()
这是我在使用 dropzone 时所做的工作,也许它也会对这里的其他人有所帮助