【问题标题】:django ajax/jquery file uploaddjango ajax/jquery 文件上传
【发布时间】:2011-11-01 00:21:42
【问题描述】:

我正在尝试复制 Alex Kuhl 在其出色帖子中给出的示例:http://kuhlit.blogspot.com/2011/04/ajax-file-uploads-and-csrf-in-django-13.html

但是,我在复制这个方面不太成功。

####### upload_page.html  

{% extends "base.html" %}
{% load i18n %}
{% block title %}Blog Post: Upload Files.{% endblock %}

{% block content %} 
<div id="maintext"> 
<p>To upload a file, click on the button below.</p>
<div id="file-uploader">
<noscript>
<p>Please enable JavaScript to use file uploader.</p>
<!-- or put a simple form for upload here -->
</noscript>
</div>
<script>
    function createUploader(){
    var uploader = new qq.FileUploader( {
        action: "{% url ajax_upload %}",
        element: $('#file-uploader')[0],
        multiple: false,
        onComplete: function( id, fileName, responseJSON ) {
          if( responseJSON.success )
        alert( "success!" ) ;
          else
        alert( "Sorry, your upload has failed! Please contact us by telephone or email." ) ;
        },
        onAllComplete: function( uploads ) {
          // uploads is an array of maps
          // the maps look like this: { file: FileObject, response: JSONServerResponse }
          alert( "All complete!" ) ;
        },
        params: {
          'csrf_token': '{{ csrf_token }}',
          'csrf_name': 'csrfmiddlewaretoken',
          'csrf_xname': 'X-CSRFToken',
        },
      } ) ;
    }

    // in your app create uploader as soon as the DOM is ready
    // don't wait for the window to load
    window.onload = createUploader;
</script>
</div>
{% endblock %}

views.py如下:

############### views.py
def upload_page( request ):
    ctx = RequestContext( request, {
        'csrf_token': get_token( request ),
    })
    return render_to_response( 'success/upload_page.html', ctx )

def save_upload( uploaded, filename, raw_data ):
    filename = settings.UPLOAD_STORAGE_DIR
    '''
    raw_data: if True, uploaded is an HttpRequest object with the file being
        the raw post data
        if False, uploaded has been submitted via the basic form
        submission and is a regular Django UploadedFile in request.FILES
    '''
    try:
        from io import FileIO, BufferedWriter
        with BufferedWriter( FileIO( filename, "wb" ) ) as dest:
            # if the "advanced" upload, read directly from the HTTP request
            # with the Django 1.3 functionality
            if raw_data:
                foo = uploaded.read( 1024 )
                while foo:
                    dest.write( foo )
                    foo = uploaded.read( 1024 )
            # if not raw, it was a form upload so read in the normal Django chunks fashion
            else:
                for c in uploaded.chunks( ):
                    dest.write( c )
            # got through saving the upload, report success
            return True
    except IOError:
        # could not open the file most likely
        pass
        return False

def ajax_upload( request ):
    if request.method == "POST":   
        if request.is_ajax( ):
            # the file is stored raw in the request
            upload = request
            is_raw = True
            # AJAX Upload will pass the filename in the querystring if it is the "advanced" ajax upload
            try:
                filename = request.GET[ 'qqfile' ]
            except KeyError:
                return HttpResponseBadRequest( "AJAX request not valid" )
        # not an ajax upload, so it was the "basic" iframe version with submission via form
        else:
            is_raw = False
            if len( request.FILES ) == 1:
                upload = request.FILES.values( )[ 0 ]
            else:
                raise Http404( "Bad Upload" )
            filename = upload.name

        # save the file
        success = save_upload( upload, filename, is_raw )

        # let Ajax Upload know whether we saved it or not
        import json
        ret_json = { 'success': success, }
        return HttpResponse( json.dumps( ret_json ) )

urls.py如下:

####### urls.py
urlpatterns = patterns('',
(r'media/(?P<path>.*)$', 'django.views.static.serve', {'document_root':   settings.MEDIA_ROOT}),
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
url(r'^$', index,name='home'),
url( r'^ajax_upload$', ajax_upload, name="ajax_upload" ),
url( r'^upload/$', upload_page, name="upload_page" ),
(r'^admin/', include(admin.site.urls)),
(r'^accounts/', include('regfields.urls')),

# Examples:
# url(r'^$', 'mysite.views.home', name='home'),
# url(r'^mysite/', include('mysite.foo.urls')),

# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line to enable the admin:
# url(r'^admin/', include(admin.site.urls)),

代码也在:http://dpaste.com/600444/

虽然一切看起来都很好,但上传总是失败。

我使用:filename = settings.UPLOAD_STORAGE_DIR,其中,UPLOAD_STORAGE_DIR 在 settings.py 中定义为 '/media/'

谁能指出我哪里出错了(对不起,我是网络编程新手,实际上以前从未使用过 JS,但可以合理地用 python 编程!)

【问题讨论】:

  • 您收到的是 ajax_upload 函数引发的“错误上传”404 还是其他 404?
  • 嗨,Alex,这就是问题所在:我既没有上传错误,也没有 403/404。当它上传时,它似乎在 OnComplete 上失败并抛出 else 错误。它几乎立即抛出错误。另一个问题:我需要更改 fileuploader.js 脚本中的任何内容吗?
  • 不,fileuploader.js 应该可以在没有任何更改的情况下工作。你用什么浏览器来测试?如果它不是正确支持 HTML5 的东西,则可能是 iframe 回退的问题。在其他情况下,如果您尝试打印 responseJSON 的值,您会得到什么(false、null 等)?正如丹尼尔在下面问的那样,你确定一个 ajax 请求会发出吗?您可以在 Firefox 中使用 Firebug(不确定 Chrome 或 Opera 的开发工具)在开始上传后查看 Ajax 流量。您还可以使用 Wireshark 或 Fiddler(Fiddler 更容易用于此目的)来查看流量。
  • 亚历克斯,再次感谢您的帮助。我在 Ubuntu 上使用 Firebox 6。我查看了萤火虫的输出,它似乎确实做了一个发布请求。我已将输出附加到:dpaste.com/600871 似乎在 ajax_upload 上说类似 NameError。这有什么意义吗? :(
  • Alex,我还发现使用 Firebug 似乎在说:异常位置:/home/foo/path/mysite/../mysite/views.py in save_upload,第 45 行和第45 基本上是:“filename = settings.UPLOAD_STORAGE_DIR”,我的 settings.py 文件中有 UPLOAD_STORAGE_DIR = '/media/'。 firebug 的更新输出开启:dpaste.com/600879

标签: django django-models django-templates django-forms django-views


【解决方案1】:

需要在判断是否为原始数据的if语句中加上return True,否则即使上传成功也会返回False:

...
if raw_data:
    foo = uploaded.read(1024)
    while foo:
        dest.write(foo)
        foo = uploaded.read(1024) 
    return True
...

【讨论】:

    【解决方案2】:

    您的 Javascript 中有错字。它应该是 {% csrf_token %} 而不是 {{ csrf_token }}

    编辑: 在您的 cmets 之后,我仔细查看了您链接的文章。

    您需要包含库fileuploader.js。它将用带有适当事件处理程序的表单替换带有 id file-uploader 的占位符 div。在纯 HTML 中创建表单将不起作用。

    我建议你看看 Github 存储库中的示例:https://github.com/alexkuhl/file-uploader/tree/master/client

    【讨论】:

    • 嗨丹尼尔..谢谢你..我已经重新编写了脚本(见上面的编辑),没有错字,我仍然得到同样的 404 错误:(
    • 是否发出了 AJAX 请求?还是带有页面重新加载的普通表单请求?另外,你能修复你的 python 代码中的缩进以使其更具可读性吗?
    • 不,我认为没有发出 AJAX 请求(您能解释一下您的意思吗?)。我现在已将代码放在 dpaste 上:dpaste.com/600371 我认为第 40 行可能有问题。我想知道我应该在这里使用什么动作“”。应该是 action="ajax_upload" 吗?非常感谢您的帮助!
    • 丹尼尔,非常感谢您的回复。我现在修改了代码,它看起来如下:dpaste.com/600413 我基本上使用 demo.html 作为起点,然后按照 [link] (kuhlit.blogspot.com/) 更改了代码的 javascript 部分2011/04/…) 亚历克斯库尔。但是,一旦我插入修改后的 JS 代码,我似乎有点失去了上传按钮。因此,我将 JS 代码更改为通过 id 获取文档到 file-uploader-demo1 .. 但是现在,当我上传文件时,它似乎总是失败.. 关于我在做什么的任何想法都非常愚蠢(?)再次感谢你的时间
    • {% csrf_token %} 呈现一个 HTML 输入小部件(和一个隐藏的 div),因此它不适用于 javascript。我会删除它,因为将其视为可接受的答案会令人困惑。
    猜你喜欢
    • 2013-03-26
    • 2014-01-16
    • 2012-04-25
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多