zoukankan      html  css  js  c++  java
  • Django文件上传下载与富文本编辑框

    django文件上传下载

    上传

    配置settings.py

    # 设定文件的访问路径,如:访问http://127.0.0.1:8000/media/就可以获取文件
    MEDIA_URL = '/media/'
    # 设置文件的存储路径,全部存储在media目录下,会和model类中的upload_to属性值拼接
    MEDIA_ROOT = os.path.join(BASE_DIR,'media')
    

    models.py

    class Img(models.Model):
        name = models.CharField(max_length=32)
        # upload_to拼接在MEDIA_ROOT后面,../media/img/article/,内容均存在该目录下
        img = models.ImageField(upload_to='img/article/',verbose_name='图片')
    

    upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <!--enctype属性值修改成"multipart/form-data"-->    
    <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <!--type类型要改成file,文件类型-->
        <input type="text" name="name">
        <input type="file" name="img">
        <button>上传</button>
    </form>
    </body>
    </html>
    
    <!--多文件上传-->
    <input type="file" name="myfiles" multiple="">
    

    views.py

    # 获取上传文件单个插入数据
    def upload(request):
    
        if request.method == 'POST':
            # 获取文件名称
            img_url = request.FILES.get("img") 
            name = request.POST.get("name")
            # 存到数据库中,并保存到指定的目录下
            img = models.Media(name=name,img=img_url)
            img.save()
    
        return render(request,"youhua.html")
    
    # 获取上传文件插入批量数据
    def upload(request):
        if request.method == 'POST':
            img_list = request.FILES.getlist('img')
            name = request.POST.get("name")
            querysetlist = []
            for img in img_list:
                querysetlist.append(models.Media(name=name,img=img))
            models.Media.objects.bulk_create(querysetlist)
    
            return HttpResponse("上传成功")
    
        return render(request, "youhua.html")
    

    ajax上传图片

    FormData上传

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    </head>
    <body>
    <input type="file" name="img" id="img">
    <input type="button" value="上传" onclick="showImg();">
    <script type="text/javascript">
        function showImg() {
            var formdata = new FormData();// 创建一个空的FormData对象,可以使用formdata.append(key,value)来添加数据。
            formdata.append('file', document.getElementById('img').files[0]);
            $.ajax({
                url: '/upload/',
                type: 'post',
                headers :{
                    'x-csrftoken': '{{ csrf_token }}', // 为了通过csrf校验,所以必须带这个过去。
                },
                data: formdata,
                // 默认值为true,默认会将发送的数据序列化以适应默认的内容类型。不想转换信息,需要设置为false
                // 此数据传输的是对象,所以不需要序列化
                processData: false,
                // 不写默认为application/x-www-form-urlencoded只能上传文本,上传文件需要用multipart/form-data类型。
                contentType: false, // 不设置内容类型
                success: function (data) {
                    alert(data)
                }
            });
        }
    </script>
    </body>
    </html>
    

    views.py

    def upload(request):
        if request.method == 'POST':
            img_url = request.FILES.get('file')
            img = models.Media(name='hello',img=img_url)
            img.save()
            ret = '上传成功'
            return HttpResponse(ret)
    
        return render(request,"youhua.html")
    

    页面展示图片

    如果涉及到下载/显示资源,就需要添加url

    from django.contrib import admin
    from django.urls import path
    from imgTest.views import uploadImg, showImg
    from django.conf.urls.static import static
    from django.conf import settings
    
    # 写法一
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('upload/', upload),
        path('showImg/', showImg)
    ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    
    # 写法二
    from django.views.static import serve
    urlpatterns = [
        ...
        path('showImg/', showImg),
        url('^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
    ]
    

    views.py

    def showImg(request):
        obj_list = models.Img.objects.all()
        print(obj_list)
    
        return render(request,'showImg.html',{"obj_list":obj_list})
    

    showImg.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {% for obj in obj_list %}
        <!--必须要带url,不然路径会错误-->
        <img src="{{ obj.img.url }}" alt="">  
    {% endfor %}
    </body>
    </html>
    

    文件下载

    自定义编写视图下载方法,主要是为了限制用户的下载内容,一定要注意限制用户的下载内容,不然知道路径连代码和数据库都下载了。
    urls.py

    from app_youhua import views
    
    urlpatterns = [
    	# ......
        url('^download/(?P<pk>d+)/$', views.download, name='download'),
    ]
    

    html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        {% load static %}
    </head>
    <body>
    {% for obj in obj_list %}
        <img src="{{ obj.img.url }}" alt="">  <!--自动将数据库中的图片全部显示,可以自定制拼接路径-->
        <a href="{% url 'download' obj.pk %}">下载</a>  <!--url反向解析-->
    {% endfor %}
    
    </body>
    </html>
    

    使用HttpResponse

    from django.http import HttpResponse, Http404
    
    def download(request, pk=None):
        obj = models.Media.objects.get(pk=pk)
        filename = str(obj.img)
        filepath = os.path.join(settings.MEDIA_ROOT, filename)
        with open(filepath, 'rb') as f:
            try:
                response = HttpResponse(f)
                response['content_type'] = "application/octet-stream"
                response['Content-Disposition'] = 'attachment; filename=%s' % filename
                return response
            except Exception:
                raise Http404
    

    HttpResponse有个很大的弊端,其工作原理是先读取文件,载入内存,然后再输出。如果下载文件很大,该方法会占用很多内存。对于下载大文件,Django更推荐StreamingHttpResponse和FileResponse方法,这两个方法将下载文件分批(Chunks)写入用户本地磁盘,先不将它们载入服务器内存。

    使用StreamingHttpResponse和FileResponse

    from django.http import FileResponse, StreamingHttpResponse
    
    def download(request, pk=None):
        obj = models.Media.objects.get(pk=pk)
        filename = str(obj.img)
        # filename = request.GET.get('file')  # 如果文件名直接通过页面传回
        filepath = os.path.join(settings.MEDIA_ROOT, filename)
        # 文件将会自动关闭,所以不需要使用with语句打开文件
        fp = open(filepath, 'rb')
        response = StreamingHttpResponse(fp)
        # response = FileResponse(fp) # 默认一次下载4096字节
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Disposition'] = 'attachment;filename="%s"' % filename
        return response
    

    文件私有化的两种方法

    如果你想实现只有登录过的用户才能查看和下载某些文件。

    • 上传文件放在media文件夹,文件名使用很长的随机字符串命名(uuid), 让用户无法根据文件名猜出这是什么文件。视图和模板里验证用户是否已登录,登录或通过权限验证后才显示具体的url。- 简单易实现,安全性不高,但对于一般项目已足够。

    • 上传文件放在非media文件夹,用户即使知道了具体文件地址也无法访问,因为Django只会给media文件夹里每个文件创建独立url资源。视图和模板里验证用户是否已登录,登录或通过权限验证后通过自己编写的下载方法下载文件。- 安全性高,但实现相对复杂。

    • 我们定义的下载方法可以下载所有文件,不仅包括.py文件,还包括不在media文件夹里的文件(比如非用户上传的文件)。比如当我们直接访问127.0.0.1:8000/file/download/file_project/settings.py/时,你会发现我们连file_project目录下的settings.py都下载了。

    # 简单举例,不让用户下载.py等结尾的文件
    from django.http import Http404,FileResponse, StreamingHttpResponse
    def download(request, pk=None):
        obj = models.Media.objects.get(pk=pk)
        filename = str(obj.img)	
        filepath = os.path.join(settings.MEDIA_ROOT, filename)
        ext = os.path.basename(file_path).split('.')[-1].lower()
        # cannot be used to download py, db and sqlite3 files.
        if ext not in ['py', 'db',  'sqlite3']:
            # 文件将会自动关闭,所以不需要使用with语句打开文件
            fp = open(filepath, 'rb')
        	response = StreamingHttpResponse(fp)
            response['content_type'] = "application/octet-stream"
            response['Content-Disposition'] = 'attachment;filename="%s"' % filename
            return response
        else:
            raise Http404
    

    django富文本编辑框

    下载

    pip install django-ckeditor
    

    注册

    INSTALLED_APPS = [
        ...
    	'ckeditor',
    	'ckeditor_uploader',
    ]
    

    settings中配置

    CKEDITOR_UPLOAD_PATH = 'ckeditor/'
    

    配置urls.py

    from ckeditor_uploader import views
    from django.views.static import serve
    
    urlpatterns = [
    	url(r'^media/(?P<path>.*)', serve, {'document_root':settings.MEDIA_ROOT}),
        # 上传文件
        url(r'^ckeditor/upload/', views.upload),
        url(r'^ckeditor/', include('ckeditor_uploader.urls')),
    ]
    

    models.py使用富文本编辑框字段

    from ckeditor_uploader.fields import RichTextUploadingField
    class Article(models.Model):
        title = models.CharField(max_length=32)
        detail = models.OneToOneField('ArticleDetail',on_delete=models.CASCADE)
        
    class ArticleDetail(models.Model):
    	content = RichTextUploadingField(verbose_name='文章详情')
    

    模板中使用

    {{ field }} 使用ModelForm富文本编辑框的字段,
    # 导入js样式,只要需要显示富文本编辑框就要导入
    <script src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
    <script src="{% static 'ckeditor/ckeditor-init.js' %}"></script>
    

    关联表同时显示富文本编辑框

    比如:编辑内容时,肯定是连同关联的详情内容一同填写。

    # 视图函数
    def article_add(request):
    	# 对两个form表单进行实例化
        form_obj = ArticleForm()
        detail_form = ArticleDetailForm()
        if request.method == 'POST':
            # 先校验并保存被关联方
            detail_form = ArticleDetailForm(request.POST)
        if detail_form.is_valid():
            detail_form.save()
            qd = request.POST.copy() # request.POST是有序字典,默认是不可编辑,所以进行深拷贝后编辑
            # 找到被关联方提交此条数据的pk
            qd['detail'] = detail_form.instance.pk
            # 校验并保存被关联方
            form_obj = ArticleForm(data=qd, files=request.FILES) # 如存在文件类,需单独传参
        if form_obj.is_valid():
            form_obj.save()
            return redirect(reverse('backend:article_list'))
        # 如果被关联方未通过校验,且关联方通过校验并保存,则删除关联方保存的数据
        if detail_form.is_valid() and detail_form.instance:
            detail_form.instance.delete()
            title = '新增文章'
        return
            render(request,'backend/article_form.html'{'form_obj':form_obj,'title':title,'detail_form':detail_form})
    
    from django import forms
    from repository import models
    
    class ArticleForm(forms.ModelForm):
        class Meta:
            model = models.Article
            fields = '__all__'
    
        def __init__(self,*args,**kwargs):
            super(ArticleForm, self).__init__(*args,**kwargs)
    
            for field in self.fields.values():
                # 用来控制不使用'form-control'样式的
                if isinstance(field.widget,forms.ClearableFileInput):
                    continue
                field.widget.attrs['class'] = 'form-control'
    
    class ArticleDetailForm(forms.ModelForm):
        class Meta:
            model = models.ArticleDetail
            fields = '__all__'
    
  • 相关阅读:
    C#删除一个字符串数组中的空字符串
    .Net后台获取客户端信息
    Java Script
    ECMAScript闭包,ECMAScript对象
    Java Script函数、变量、对象
    JavaScript3
    JavaScript-2
    变量
    8.22收获
    html
  • 原文地址:https://www.cnblogs.com/liuweida/p/11810360.html
Copyright © 2011-2022 走看看