Làm thế nào để tải lên một tập tin trong Django? [đóng cửa]


668

Là một người mới chơi Django, tôi gặp khó khăn khi tạo một ứng dụng tải lên trong Django 1.3. Tôi không thể tìm thấy bất kỳ ví dụ / đoạn trích cập nhật nào. Ai đó có thể đăng một mã ví dụ tối thiểu nhưng đầy đủ (Model, View, Template) để làm như vậy không?

Câu trả lời:


1273

Phew, tài liệu Django thực sự không có ví dụ hay về điều này. Tôi đã dành hơn 2 giờ để đào tất cả các mảnh để hiểu cách thức hoạt động của nó. Với kiến ​​thức đó, tôi đã thực hiện một dự án có thể tải lên các tệp và hiển thị chúng dưới dạng danh sách. Để tải xuống nguồn cho dự án, hãy truy cập https://github.com/axelpale/minimal-django-file-upload-example hoặc sao chép nó:

> git clone https://github.com/axelpale/minimal-django-file-upload-example.git

Cập nhật 2013-01-30: Nguồn tại GitHub cũng đã triển khai cho Django 1.4 ngoài 1.3. Mặc dù có một vài thay đổi, hướng dẫn sau đây cũng hữu ích cho 1.4.

Cập nhật 2013-05-10: Triển khai Django 1.5 tại GitHub. Những thay đổi nhỏ trong chuyển hướng trong url url và việc sử dụng thẻ mẫu url trong list.html. Cảm ơn hubert3 cho những nỗ lực.

Cập nhật 2013-12-07: Django 1.6 được hỗ trợ tại GitHub. Một lần nhập đã thay đổi trong myapp / urls.py. Cảm ơn đến Arthedian .

Cập nhật 2015/03/17: Django 1.7 được hỗ trợ tại GitHub, nhờ aronysidoro .

Cập nhật 2015-09-04: Django 1.8 được hỗ trợ tại GitHub, nhờ vào nerogit .

Cập nhật 2016/07/03: Django 1.9 được hỗ trợ tại GitHub, nhờ daavvenerogit

Cây dự án

Một dự án Django 1.3 cơ bản với ứng dụng duy nhất và phương tiện / thư mục để tải lên.

minimal-django-file-upload-example/
    src/
        myproject/
            database/
                sqlite.db
            media/
            myapp/
                templates/
                    myapp/
                        list.html
                forms.py
                models.py
                urls.py
                views.py
            __init__.py
            manage.py
            settings.py
            urls.py

1. Cài đặt: myproject / settings.py

Để tải lên và phục vụ các tệp, bạn cần chỉ định nơi Django lưu trữ các tệp đã tải lên và từ URL nào Django phục vụ chúng. MEDIA_ROOT và MEDIA_URL theo mặc định trong settings.txt nhưng chúng trống. Xem các dòng đầu tiên trong Django Manage Files để biết chi tiết. Nhớ cũng đặt cơ sở dữ liệu và thêm myapp vào INSTALLED_APPS

...
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'database.sqlite3'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
...
INSTALLED_APPS = (
    ...
    'myapp',
)

2. Mô hình: myproject / myapp / model.py

Tiếp theo bạn cần một mô hình với FileField. Trường cụ thể này lưu trữ các tệp, ví dụ như phương tiện / tài liệu / 2011/12/24 / dựa trên ngày hiện tại và MEDIA_ROOT. Xem tài liệu tham khảo FileField .

# -*- coding: utf-8 -*-
from django.db import models

class Document(models.Model):
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')

3. Hình thức: myproject / myapp / Forms.py

Để xử lý tải lên độc đáo, bạn cần một hình thức. Hình thức này chỉ có một lĩnh vực nhưng thế là đủ. Xem tài liệu tham khảo Form FileField để biết chi tiết.

# -*- coding: utf-8 -*-
from django import forms

class DocumentForm(forms.Form):
    docfile = forms.FileField(
        label='Select a file',
        help_text='max. 42 megabytes'
    )

4. Xem: myproject / myapp / view.py

Một cái nhìn nơi tất cả các phép thuật xảy ra. Hãy chú ý cách request.FILESxử lý. Đối với tôi, thật khó để phát hiện ra thực tế request.FILES['docfile']có thể được lưu vào các mô hình.FileField giống như vậy. Save () của mô hình tự động xử lý việc lưu trữ tệp vào hệ thống tệp.

# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from myproject.myapp.models import Document
from myproject.myapp.forms import DocumentForm

def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            newdoc = Document(docfile = request.FILES['docfile'])
            newdoc.save()

            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('myapp.views.list'))
    else:
        form = DocumentForm() # A empty, unbound form

    # Load documents for the list page
    documents = Document.objects.all()

    # Render list page with the documents and the form
    return render_to_response(
        'myapp/list.html',
        {'documents': documents, 'form': form},
        context_instance=RequestContext(request)
    )

5. URL dự án: myproject / urls.py

Django không phục vụ MEDIA_ROOT theo mặc định. Điều đó sẽ nguy hiểm trong môi trường sản xuất. Nhưng trong giai đoạn phát triển, chúng ta có thể cắt ngắn. Hãy chú ý đến dòng cuối cùng. Dòng đó cho phép Django phục vụ các tệp từ MEDIA_URL. Điều này chỉ hoạt động trong giai đoạn phát triển.

Xem tài liệu tham khảo django.conf.urls.static.static để biết chi tiết. Xem thêm cuộc thảo luận này về việc phục vụ các tập tin phương tiện truyền thông .

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = patterns('',
    (r'^', include('myapp.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

6. URL ứng dụng: myproject / myapp / urls.py

Để làm cho chế độ xem có thể truy cập, bạn phải chỉ định các url cho nó. Không có gì đặc biệt ở đây.

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url

urlpatterns = patterns('myapp.views',
    url(r'^list/$', 'list', name='list'),
)

7. Mẫu: myproject / myapp / samples / myapp / list.html

Phần cuối cùng: mẫu cho danh sách và hình thức tải lên bên dưới nó. Biểu mẫu phải có thuộc tính enctype được đặt thành "nhiều dữ liệu / biểu mẫu dữ liệu" và phương thức được đặt thành "bài đăng" để có thể tải lên Django. Xem tài liệu Tải lên tệp để biết chi tiết.

FileField có nhiều thuộc tính có thể được sử dụng trong các mẫu. Ví dụ: {{document.docfile.url}} và {{document.docfile.name}} như trong mẫu. Xem thêm về những điều này trong Sử dụng tệp trong bài viết mô hìnhTài liệu đối tượng Tệp .

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Minimal Django File Upload Example</title>   
    </head>
    <body>
    <!-- List of uploaded documents -->
    {% if documents %}
        <ul>
        {% for document in documents %}
            <li><a href="{{ document.docfile.url }}">{{ document.docfile.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No documents.</p>
    {% endif %}

        <!-- Upload form. Note enctype attribute! -->
        <form action="{% url 'list' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ form.non_field_errors }}</p>
            <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
            <p>
                {{ form.docfile.errors }}
                {{ form.docfile }}
            </p>
            <p><input type="submit" value="Upload" /></p>
        </form>
    </body>
</html> 

8. Khởi tạo

Chỉ cần chạy syncdb và máy chủ.

> cd myproject
> python manage.py syncdb
> python manage.py runserver

Các kết quả

Cuối cùng, mọi thứ đã sẵn sàng. Trên môi trường phát triển Django mặc định, có thể xem danh sách các tài liệu được tải lên tạilocalhost:8000/list/ . Hôm nay các tệp được tải lên / path / to / myproject / media / Documents / 2011/12/17 / và có thể được mở từ danh sách.

Tôi hy vọng câu trả lời này sẽ giúp được ai đó nhiều như nó sẽ giúp tôi.


9
Tìm thấy vị trí trong tài liệu django hiển thị tệp tải lên. Ví dụ trong câu trả lời này là tuyệt vời, nhưng thông tin trong tài liệu django sẽ được cập nhật với các bản phát hành mới. docs.djangoproject.com/en/dev/topics/http/file-uploads
TaiwanGrapefbeanTea

1
Ví dụ này không hoạt động với Django "1.5". Trong HTML {% url list %}trở thành {% url "list" %}.
Matthieu Riegler

4
Cảm ơn rât nhiều . Đây đúng là công việc thực sự đối với tôi. Tuy nhiên, đối với những người xem sắp tới, bạn nên kiểm tra mã trong gitHub để có khả năng tương thích tốt nhất với các phiên bản mới của Python và Django. Ví dụ: nên thay thế các lượt xem, render_to_response () bằng render (request, ...,) để tránh lỗi CSRF. Chúc mừng.
Huy Than

1
Có thể làm điều này mà không có HÌNH THỨC?
Roel

1
Tệp có thể là .zip hoặc các tệp nén khác không?
qg_java_17137

75

Nói chung, khi bạn đang cố gắng 'chỉ lấy một ví dụ hoạt động', tốt nhất là 'chỉ bắt đầu viết mã'. Không có mã ở đây để giúp bạn, vì vậy nó giúp trả lời câu hỏi nhiều hơn cho chúng tôi.

Nếu bạn muốn lấy một tập tin, bạn cần một cái gì đó như thế này trong một tập tin html ở đâu đó:

<form method="post" enctype="multipart/form-data">
    <input type="file" name="myfile" />
    <input type="submit" name="submit" value="Upload" />
</form>

Điều đó sẽ cung cấp cho bạn nút duyệt, nút tải lên để bắt đầu hành động (gửi biểu mẫu) và lưu ý mã hóa để Django biết để cung cấp cho bạn request.FILES

Trong chế độ xem ở đâu đó, bạn có thể truy cập tệp với

def myview(request):
    request.FILES['myfile'] # this is my file

Có một lượng lớn thông tin trong các tài liệu tải lên tập tin

Tôi khuyên bạn nên đọc kỹ trang và chỉ bắt đầu viết mã - sau đó quay lại với các ví dụ và ngăn xếp dấu vết khi nó không hoạt động.


10
Cảm ơn Henry. Trên thực tế tôi đã đọc các tài liệu và đã viết một số mã nhưng vì các tài liệu có một số lỗ hổng (ví dụ "từ một nơi nào đó nhập handle_uploaded_file") và mã của tôi bị lỗi, tôi nghĩ rằng sẽ tốt hơn nhiều nếu tôi có thể bắt đầu từ một ví dụ hoạt động .
qliq

26
Đồng ý với qliq. Một ví dụ làm việc đơn giản là cách hiệu quả nhất để đưa người mới đến, không phải tài liệu
Philip007

11
Những enctype="multipart/form-data"gì tôi cần để làm cho công việc này, cảm ơn!
john-charles

5
Chỉ cần không bỏ lỡ {% csrf_token%} trong các thẻ biểu mẫu.
jonincanada

có thể làm điều này mà KHÔNG CÓ HÌNH THỨC TỪ FORMS.PY?
Roel

71

Bản giới thiệu

Xem repo github , hoạt động với Django 3

Một ví dụ tải lên tệp Django tối thiểu

1. Tạo một dự án django

Chạy startproject ::

$ django-admin.py startproject sample

bây giờ một thư mục ( mẫu ) được tạo ra.

2. tạo một ứng dụng

Tạo một ứng dụng ::

$ cd sample
$ python manage.py startapp uploader

Bây giờ một thư mục ( uploader) với các tệp này được tạo ::

uploader/
  __init__.py
  admin.py
  app.py
  models.py
  tests.py
  views.py
  migrations/
    __init__.py

3. Cập nhật cài đặt

Mở sample/settings.pyadd 'uploader'để INSTALLED_APPSvà thêm MEDIA_ROOTMEDIA_URL, tức ::

INSTALLED_APPS = [
    'uploader',
    ...<other apps>...      
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

4. Cập nhật url

trong sample/urls.pythêm ::

...<other imports>...
from django.conf import settings
from django.conf.urls.static import static
from uploader import views as uploader_views

urlpatterns = [
    ...<other url patterns>...
    path('', uploader_views.UploadView.as_view(), name='fileupload'),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5. Cập nhật mô hình

cập nhật uploader/models.py::

from django.db import models
class Upload(models.Model):
    upload_file = models.FileField()    
    upload_date = models.DateTimeField(auto_now_add =True)

6. Cập nhật lượt xem

cập nhật uploader/views.py::

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Upload
class UploadView(CreateView):
    model = Upload
    fields = ['upload_file', ]
    success_url = reverse_lazy('fileupload')
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['documents'] = Upload.objects.all()
        return context

7. tạo mẫu

Tạo một mẫu thư mục / trình tải lên / mẫu / trình tải lên

Tạo một tệp upload_form.html tức là sample/uploader/templates/uploader/upload_form.html::

<div style="padding:40px;margin:40px;border:1px solid #ccc">
    <h1>Django File Upload</h1>
    <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Submit</button>
    </form><hr>
    <ul>
    {% for document in documents %}
        <li>
            <a href="{{ document.upload_file.url }}">{{ document.upload_file.name }}</a>
            <small>({{ document.upload_file.size|filesizeformat }}) - {{document.upload_date}}</small>
        </li>
    {% endfor %}
    </ul>
</div>

8. Đồng bộ hóa cơ sở dữ liệu

Đồng bộ hóa cơ sở dữ liệu và máy chủ ::

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver

truy cập http: // localhost: 8000 /


2
hoàn hảo ngoại trừ dòng cuối cùng - nên là localhost.com:8000/upload > Điều này hoạt động cho django 1.6 và Python 3.3.
Steve

5
+1 cho mẫu thiết kế ứng dụng django có thể tái sử dụng
Marcel

1
Akseli đã sử dụng một FileFieldthời gian suhail sử dụng một ImageField, ai đó có thể vui lòng giải thích các lựa chọn?
dvtan

@dtgq Mình cập nhật câu trả lời để sử dụng với FileField. ImageFieldphải cho hình ảnh chỉ tải lên. Bản cập nhật sẽ hoạt động với Django 1.11.
suhailvs

đã thử nghiệm trên Django 2.0 và hoạt động hoàn hảo
diek

29

Tôi phải nói rằng tôi tìm thấy các tài liệu tại django khó hiểu. Ngoài ra đối với ví dụ đơn giản nhất tại sao các hình thức được đề cập? Ví dụ tôi có để làm việc trong lượt xem là: -

for key, file in request.FILES.items():
    path = file.name
    dest = open(path, 'w')
    if file.multiple_chunks:
        for c in file.chunks():
            dest.write(c)
    else:
        dest.write(file.read())
    dest.close()

Tệp html trông giống như mã bên dưới, mặc dù ví dụ này chỉ tải lên một tệp và mã để lưu các tệp xử lý nhiều: -

<form action="/upload_file/" method="post" enctype="multipart/form-data">{% csrf_token %}
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>

Những ví dụ này không phải là mã của tôi, chúng đã được lấy từ hai ví dụ khác mà tôi tìm thấy. Tôi là người mới bắt đầu với django nên rất có khả năng tôi đang thiếu một số điểm chính.


3
+1 khi không sử dụng a FileFieldvà a model.Form. Đối với người mới bắt đầu (và đối với các tác vụ tầm thường), xử lý thủ công các tệp được tải lên như hiển thị ở trên ít gây nhầm lẫn.
AneesAhmed777

Dest = open (path, 'wb') khi tập tin ghi bằng byte
Bipul Roy

20

Tôi cũng có yêu cầu tương tự. Hầu hết các ví dụ trên mạng đang yêu cầu tạo mô hình và tạo các biểu mẫu mà tôi không muốn sử dụng. Đây là mã cuối cùng của tôi.

if request.method == 'POST':
    file1 = request.FILES['file']
    contentOfFile = file1.read()
    if file1:
        return render(request, 'blogapp/Statistics.html', {'file': file1, 'contentOfFile': contentOfFile})

Và trong HTML để tải lên, tôi đã viết:

{% block content %}
    <h1>File content</h1>
    <form action="{% url 'blogapp:uploadComplete'%}" method="post" enctype="multipart/form-data">
         {% csrf_token %}
        <input id="uploadbutton" type="file" value="Browse" name="file" accept="text/csv" />
        <input type="submit" value="Upload" />
    </form>
    {% endblock %}

Sau đây là HTML hiển thị nội dung của tệp:

{% block content %}
    <h3>File uploaded successfully</h3>
    {{file.name}}
    </br>content = {{contentOfFile}}
{% endblock %}

tốt vì đôi khi người ta chỉ muốn sử dụng nội dung của tệp không lưu tệp tải lên ...
nemeisfixx

17

Mở rộng trên ví dụ của Henry :

import tempfile
import shutil

FILE_UPLOAD_DIR = '/home/imran/uploads'

def handle_uploaded_file(source):
    fd, filepath = tempfile.mkstemp(prefix=source.name, dir=FILE_UPLOAD_DIR)
    with open(filepath, 'wb') as dest:
        shutil.copyfileobj(source, dest)
    return filepath

Bạn có thể gọi handle_uploaded_filechức năng này từ chế độ xem của bạn với đối tượng tệp được tải lên. Điều này sẽ lưu tệp với một tên duy nhất (tiền tố với tên tệp của tệp được tải lên ban đầu) trong hệ thống tệp và trả về đường dẫn đầy đủ của tệp đã lưu. Bạn có thể lưu đường dẫn trong cơ sở dữ liệu và làm một cái gì đó với tệp sau này.


Imran, tôi đã thử mã của bạn trên chế độ xem của tôi nhưng gặp lỗi này: 'Đối tượng WSGIRequest' không có thuộc tính 'tên'.
qliq

2
request.FILES['myfile']Truyền đối tượng tệp đã tải lên ( ) cho handle_uploaded_file, không phải requestchính nó.
Imran

Tôi có thể lưu nó trực tiếp vào cơ sở dữ liệu không? stackoverflow.com/questions/24705246/ từ
AlexandruC

Bằng cách sử dụng prefix=source.namenó đã thêm các ký tự phụ ở cuối tệp, làm rối tung phần mở rộng tệp. Ví dụ: upload.csvđã đổi thành upload.csv5334. Thay đổi nó để suffix=source.namesửa nó cho tôi.
Tahreem Iqbal

13

Ở đây nó có thể giúp bạn: tạo một trường tệp trong mô hình của bạn

Để tải lên tệp (trong admin.txt của bạn):

def save_model(self, request, obj, form, change):
    url = "http://img.youtube.com/vi/%s/hqdefault.jpg" %(obj.video)
    url = str(url)

    if url:
        temp_img = NamedTemporaryFile(delete=True)
        temp_img.write(urllib2.urlopen(url).read())
        temp_img.flush()
        filename_img = urlparse(url).path.split('/')[-1]
        obj.image.save(filename_img,File(temp_img)

và sử dụng trường đó trong mẫu của bạn cũng.


1
Điều này rất hữu ích, khi bạn phải tiết chế thủ công các tệp bạn muốn lưu. Nếu vậy, bạn cũng có thể cần phần này: docs.djangoproject.com/en/dev/topics/files/#the-file-object
kecske

11

Bạn có thể tham khảo các ví dụ về máy chủ trong Fine Uploader, có phiên bản django. https://github.com/FineUploader/server-examples/tree/master/python/django-fine-uploader

Nó rất thanh lịch và quan trọng nhất, nó cung cấp lib js đặc trưng. Mẫu không được bao gồm trong các ví dụ máy chủ, nhưng bạn có thể tìm thấy bản demo trên trang web của nó. Trình tải lên tốt: http://fineuploader.com/demos.html

django-fine-uploader

lượt xem

UploadView gửi bài và xóa yêu cầu đến xử lý tương ứng.

class UploadView(View):

    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(UploadView, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        """A POST request. Validate the form and then handle the upload
        based ont the POSTed data. Does not handle extra parameters yet.
        """
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_upload(request.FILES['qqfile'], form.cleaned_data)
            return make_response(content=json.dumps({ 'success': True }))
        else:
            return make_response(status=400,
                content=json.dumps({
                    'success': False,
                    'error': '%s' % repr(form.errors)
                }))

    def delete(self, request, *args, **kwargs):
        """A DELETE request. If found, deletes a file with the corresponding
        UUID from the server's filesystem.
        """
        qquuid = kwargs.get('qquuid', '')
        if qquuid:
            try:
                handle_deleted_file(qquuid)
                return make_response(content=json.dumps({ 'success': True }))
            except Exception, e:
                return make_response(status=400,
                    content=json.dumps({
                        'success': False,
                        'error': '%s' % repr(e)
                    }))
        return make_response(status=404,
            content=json.dumps({
                'success': False,
                'error': 'File not present'
            }))

biểu mẫu

class UploadFileForm(forms.Form):

    """ This form represents a basic request from Fine Uploader.
    The required fields will **always** be sent, the other fields are optional
    based on your setup.
    Edit this if you want to add custom parameters in the body of the POST
    request.
    """
    qqfile = forms.FileField()
    qquuid = forms.CharField()
    qqfilename = forms.CharField()
    qqpartindex = forms.IntegerField(required=False)
    qqchunksize = forms.IntegerField(required=False)
    qqpartbyteoffset = forms.IntegerField(required=False)
    qqtotalfilesize = forms.IntegerField(required=False)
    qqtotalparts = forms.IntegerField(required=False)

7

Không chắc chắn nếu có bất kỳ nhược điểm nào đối với phương pháp này nhưng thậm chí còn tối thiểu hơn, trong lượt xem:

entry = form.save()

# save uploaded file
if request.FILES['myfile']:
    entry.myfile.save(request.FILES['myfile']._name, request.FILES['myfile'], True)

0

Tôi đã đối mặt với vấn đề tương tự, và giải quyết bằng trang web quản trị django.

# models
class Document(models.Model):
    docfile = models.FileField(upload_to='documents/Temp/%Y/%m/%d')

    def doc_name(self):
        return self.docfile.name.split('/')[-1] # only the name, not full path

# admin
from myapp.models import Document
class DocumentAdmin(admin.ModelAdmin):
    list_display = ('doc_name',)
admin.site.register(Document, DocumentAdmin)

[nhập mô tả liên kết tại đây] [1] [nhập mô tả liên kết tại đây] [2] [1]: youtu.be/0tNZB3dyopY [2]: youtu.be/klhMYMc3PlY
uda123
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.