【问题标题】:Django and Dropzone.jsDjango 和 Dropzone.js
【发布时间】:2015-11-04 03:45:09
【问题描述】:

当我使用 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 中得到这个:

&lt;MultiValueDict: {u'file_upload[]': [&lt;InMemoryUploadedFile: normal.PNG (image/png)&gt;]}&gt;

当我上传两个时,我在 request.FILES 中得到了这个:

&lt;MultiValueDict: {u'file_upload[]': [&lt;TemporaryUploadedFile: normal.PNG (image/png)&gt;]}&gt;

尽管是两个文件,但它只显示一个,但将它们都添加到数据库中(都没有文件,只有 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 为空,而其他文件则不是。此外,似乎我的视图被多次调用(根据日志输出),这是正常的,除了它应该是一个帖子然后重定向到一个 get,但它似乎有多个帖子请求。我检查了 chrome 中的开发工具,我只看到一个,但奇怪的是,每次提交时它都会输出我的日志语句两次。我知道问题可能在我看来,但我已经尝试了很多东西,但无法找出问题所在。

有人有什么想法吗?

【问题讨论】:

    标签: javascript python django file-upload dropzone.js


    【解决方案1】:

    我自己正在使用 Dropzone 和 Django 为每个上传的文件创建 Image 对象,这似乎类似于您想要做的事情。我想指出一些我经历过的事情,并告诉你我是如何做到的,看看是否有帮助。

    你需要什么

    为了在数据库中为使用 Dropzone 上传的文件创建记录,您需要做的事情是:

    1. Dropzone HTML 表单
    2. Dropzone 的 Javascript 初始化。
    3. 处理上传文件的 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,
        )
    

    这应该足以创建您想要的对象。

    Dropzone Javascript

    似乎在 Click 事件监听器中你添加到你必须添加的提交按钮

    e.preventDefault();
    e.stopPropagation();
    

    在调用 processQueue() 以避免双重提交之前。

    至于sendingmultiplesuccessmultipleerrormultiple,你想在那里发生什么? cmets 只是用来指示这些事件何时被触发。

    我个人使用:

    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();
    });
    

    当然,你可以为所欲为。

    最后,您打算对&lt;script&gt; 标记中的最后一行做什么?我不太明白,看起来如果您想在成功时重新处理队列。它似乎不属于那里。

    如果有任何问题,请发表评论,但此设置对我来说很好。

    【讨论】:

    • 这看起来很完美。我认为有了这个和一个小的配置修复它会起作用。正如你所说,表格几乎没用。我会试一试
    • 这个方法是合理的,我可以保证它对我有用。当然你会得到它的工作,但如果有问题,它可能是一个小怪癖,祝你好运。
    • 当我将 request.FILES 打印到日志中时,我得到: (DEBUG) request.FILES: 。但我需要弄清楚为什么表单验证失败或如何不需要它,因为正如你所说的表单并没有做太多。但我知道 request.FILES 不是没有,因为它通过了检查。我不确定该怎么做。为什么不将文件捕获到该字典中?如果字典不为空,我相信它会起作用。就像form.is_valid() 一样,我完全糊涂了。
    • 当表单验证失败时,您可以在form.errors 中看到错误。也许检查一下。我无法为您提供更多帮助,但我会给您这个提示:使用 Ctrl+Shift+i 在 Chrome 或 Firefox 中打开调试面板,转到网络部分并查找文件上传请求。如果文件被正确发送,问题出在表单上(您应该删除它,一个简单的视图就足够了。尝试通过使用import pdb; pdb.set_trace() 进行调试来查看发生了什么)。否则 Dropzone 代码有问题(解决问题应该不难;使用浏览器的开发工具)。
    • 如果你能帮我解决剩下的问题,我会再奖励 100 赏金。
    【解决方案2】:

    在您的 Javascript 中,我认为您想将其用于 paramName:

    paramName: "file_upload",
    

    为了让 Django Form 或 ModelForm 识别上传的文件。

    还要确保上传请求使用的是 multipart/form-data 内容类型。

    另外,试试这个而不是“get_list”:

    dz_files = request.FILES.getlist("file_upload")
    

    【讨论】:

    • 试一试,但它始终未能通过 form.is_valid() 检查
    • 我收到一个 POST 响应,但当我删除检查时,它确实执行 302 重定向,但 getlist('file_upload)' 返回 [] 我假设我可以修复 form.is_valid() 失败覆盖表单方法,但我仍然不知道为什么我一直得到一个空列表。我收到错误“dropzone 已附加”。不确定这是否相关。
    • 不管表单是否工作,如果文件被正确发送到服务器,request.FILES 应该有一些东西。这就是我们需要开始的地方。
    • 看看这个看看你是否可以摆脱“dropzone already attach”错误:github.com/enyo/dropzone/wiki/…
    【解决方案3】:

    我不知道我现在展示的内容是否会特别帮助你,但也许会对其他人有所帮助,使用 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 方面所做的工作,也许它也会帮助这里的其他人

    【讨论】:

      猜你喜欢
      • 2019-11-15
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      • 1970-01-01
      • 1970-01-01
      • 2017-05-31
      • 2013-07-02
      • 1970-01-01
      相关资源
      最近更新 更多