最近在捣鼓 Django 文件上传和下载,过程还好,挺简单,但其中遇到好多问题,折腾好久。把自己实现的方法分享下。
好,先来说上传吧。
参考 Django 官方文档:https://docs.djangoproject.com/en/1.7/topics/http/file-uploads/
由于之后还要考虑下载的问题,那我们就多做一步,将文件记录在数据库。
model
那么,首先写一个用于记录上传文件的 model :
1 2 3
| class UploadFile(models.Model): name = models.CharField(max_length=50) file = models.FileField(upload_to = "uploads/%Y/%m")
|
其中包含一个 CharField 字段,用于存放上传文件名,还有一个 FileField 字段,用来存放 文件本身(其实是文件的路径)。upload_to 参数是用来设置上传文件的保存路径,此处是按上传日期的年月来归档。
路由配置
配置 settings 文件,添加如下两行:
1 2
| MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
如此,文件将上传到 根目录下的 media/uploads/… 下。
接下来写 html 模板 index.html :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> {% for file in file_list %} <li><a href="#">{{ file.name }}</a></li> {% endfor %} <form enctype="multipart/form-data" method="POST" action="{% url 'uploads' %}"> {% csrf_token %} <input type="file" name="file" /> <br /> <input type="submit" value="上传文件" /> </form> </body> </html>
|
配置 URL,urls.py 中添加:
1 2
| url(r'^$',index), url(r'^uploads', uploads, name ="uploads")
|
后台逻辑
接着重点来了,去写具体的方法,views.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| def index(request): file_list = UploadFile.objects.all() return render(request, "index.html", {"file_list" : file_list}) def handle_uploaded_file(f, path): t = path.split("/") file_name = t[-1] extensions = file_name.split(".")[1] t.remove(t[-1]) t.remove(t[0]) path = "/".join(t) try: if not os.path.exists(path): os.makedirs(path) file_name = path + "/" + file_name +"." + extensions print file_name destination = open(file_name, 'wb+') for chunk in f.chunks(): destination.write(chunk) destination.close() except Exception, e: print e return file_name def uploads(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): upload_file = UploadFile.objects.create(name = request.FILES['file'].name, file = request.FILES['file']) path = upload_file.file.url handle_uploaded_file(request.FILES['file'], path) upload_file.save() return redirect("blog.views.index") else: form = UploadFileForm() return render(request, 'index.html', {'form': form})
|
到这按说已经可以了,但,在之后的下载中出了问题,是因为文件名的问题,如果文件名是中文的,在传递的过程中编码的问题就不得不考虑,而且很是烦人,所以在这,我们要避过使用中文文件名的问题。好,查一查,Django 上传文件重命名的问题。
方法来自这:link.
赶紧实践一下。
首先修改settings.py的配置,通过查看源文件,可以看到DEFAULT_FILE_STORAGE默认指向的是FileStorage,我们可以修改指向,然后重写save方法。
settings 文件中添加:
1
| DEFAULT_FILE_STORAGE = "mysite.customfilefield.storage.FileStorage"
|
mysite 是工程名,在工程文件夹下新建文件夹 customfilefield ,将下面的 storage.py 文件放在它的下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| coding:utf-8 -*- from django.core.files.storage import FileSystemStorage from django.conf import settings import os, time class FileStorage(FileSystemStorage): def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL): super(FileStorage, self).__init__(location, base_url) def _save(self, name, content): ext = os.path.splitext(name)[1] d = os.path.dirname(name) fn = str(time.time()) fn = ("").join(fn.split(".")) name = os.path.join(d, fn + ext) return super(FileStorage, self)._save(name, content)
|
好了,这样上传的文件的名字就会以上传的时间来命名了。
上传就到这里了。