Django file upload and download rich text edit box

django file upload and download

Upload

Configuration 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 upload pictures

FormData upload

<!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")

Pages show pictures

If it comes to downloading / display resources, we need to add 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>

document dowload

Custom written views download method, mainly to limit users to download content, must pay attention to limit users to download content , or even know the path code and database are downloaded.
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>

Use 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 there is a serious shortcoming, its working principle is to read the file into memory and then output. If the downloaded file is large, this method can take up a lot of memory. For downloading large files, Django also recommend StreamingHttpResponse and FileResponse method, the two methods to download files in batches (Chunks) written to a user's local disk, without loading them first server memory.

Use StreamingHttpResponse and 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

Two methods of privatization file

If you want to achieve only logged in users can view and download certain files.

  • Upload files in the media folder, file name, use a long random string name (uuid), so that the user can not guess the file name What is file. Views and templates to verify whether the user is logged in, login or displayed by permission only after verifying specific url. - simple and easy to implement, security is not high, but for the average project is sufficient.

  • Upload files on non-media folder, even if the user knows the specific address can not access the file because Django only to the media folder to create a separate file for each url resources. View and verify whether the template user is logged in, login or verified by the authority by download method I have written to download the file. - safe, but the implementation is relatively complex.
  • Download the way we defined can download all the files, including not only .py files, but file (such as non-users to upload files) is not in the media folder. For example, when we direct access 127.0.0.1:8000/file/download/file_project/settings.py/, you will find that we can not even file_project settings.py under the directory are downloaded.

# 简单举例,不让用户下载.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 rich text edit box

download

pip install django-ckeditor

registered

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

configure settings

CKEDITOR_UPLOAD_PATH = 'ckeditor/'

Configuration 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 use rich text edit box field

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='文章详情')

Used in the template

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

Associated table also shows the rich text edit box

For example: When editing content, certainly fill in the details together, along with the associated content.

# 视图函数
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__'

Guess you like

Origin www.cnblogs.com/liuweida/p/11810360.html