前端js实现文件多次添加累加上传和选择删除(django+js)- 添加累加文件上传 (一)

news2024/11/14 2:28:10

前言

原本的多文件上传功能在选择文件时,只能通过同一范围的鼠标框选或者ctrl/shift多选取选择文件,这样选择文件很不灵活,而且在确定之后如果漏选了文件,再次点击上传按钮时会清空表单里的文件信息,只能重复之前的操作去选文件再加上漏选的文件,十分不方便。

所以在大部分网站上选择图片上传都是可以累加的,就是在第一次选择完文件点击确定后,第二次再点击上传按钮选文件会自动跟第一次的接在一起,然后展示在界面上,方便用户单独进行删除。文章的主旨就是在不用插件的情况下实现这样的效果。

一.实现效果

可多次添加多个文件,点击删除图标可进行文件的移除

1.第一次添加1个文件

在这里插入图片描述

2.第二次添加2个文件

在这里插入图片描述

二.代码实现

该项目用的是django+js,前端部分基本都差不多,废话不多说,直接上代码吧。
这里贴主要的代码,如有问题可评论或私信。

1.html部分

form表单

{% block main %}
<!--根据自己项目目录路径引入自定义上传的组件的样式fileUpload.css文件-->
<link href="../../../static/public/css/fileUpload.css" rel="stylesheet"/>
...
	<form id="dataStructureForm" method="post" class="container">
		
		...
	    <!--add attachment-->
	    <div class="form-group">
	        <label class="font-weight-bold">Add attachments:</label>
	        <input class="choose-file-clip" type="file" id="selectFile" name="uploadFileBase" multiple="multiple">
	        <label for="selectFile" class="choose-file"><i class="fa fa-paperclip"></i> Choose File</label>
	        <div id="showFileName" class="upfile-list-mes"></div>
	        <span class="form-text text-muted">所有文件大小不超过50M。</span>
	    </div>
	    ...
	</form>
	...
{% endblock %}

<!-- 此处为django模板加载js的写法 -->
{% block script %}
    <script src="../../../static/pages/xxxxxxxxxxxx/xxxxxxxxx/addDataStructure.js"
            type="text/javascript"></script>
{% endblock %}

自定义上传的组件的样式fileUpload.css文件

.choose-file-clip {
    position: absolute;
    clip: rect(0, 0, 0, 0);
}

.choose-file {
    padding: 5px 20px;
    font-size: larger;
    background: #2ECA8B;
    border-radius: 4px;
    color: white;
    cursor: pointer;
}

.upfile-list-mes {
    color: #00bfff;
}

.icon-remove {
    cursor: pointer;
    color: #FD5373;
    font-size: medium;
}

.file-name {
    /*color: #00bfff;*/
    color: #161C2D;
    font-size: medium;
    margin: 0;
}

2.js部分

最重要的部分,文件追加逻辑 fileUpload.js

var fileLists = [];
var fileNameList = []

// 选择文件
$("#selectFile").on('change', function (event) {
    let _files = this.files;
    // console.log(_files)
    if (_files.length > 0) {
        _files = Array.from(_files); //将伪数组专为真数组修改
        for (let i = 0; i < _files.length; i++) {
            // 文件去重处理
            if (fileNameList.indexOf(_files[i].name) === -1) {
                console.log('not found-->' + _files[i].name)
                fileLists.push(_files[i]);
                fileNameList.push(_files[i].name)
                let html = "<p class='file-name'>" + _files[i].name + sizeToStr(_files[i].size) + "&nbsp;&nbsp;&nbsp;&nbsp;   <span class='icon-remove'><i class='fa fa-trash'></i></span></p>"
                $('.upfile-list-mes').append(html);
            }
        }
    }
    console.log(fileLists)
})

function sizeToStr(size) {
    var data = "";
    if (size < 0.1 * 1024) { //如果小于0.1KB转化成B
        data = size.toFixed(2) + "B";
    } else if (size < 0.1 * 1024 * 1024) { //如果小于0.1MB转化成KB
        data = (size / 1024).toFixed(2) + "KB";
    } else if (size < 0.1 * 1024 * 1024 * 1024) { //如果小于0.1GB转化成MB
        data = (size / (1024 * 1024)).toFixed(2) + "MB";
    } else { //其他转化成GB
        data = (size / (1024 * 1024 * 1024)).toFixed(2) + "GB";
    }
    var sizestr = data + "";
    var len = sizestr.indexOf("\.");
    var dec = sizestr.substr(len + 1, 2);
    if (dec === "00") { //当小数点后为00时 去掉小数部分
        return '&nbsp;&nbsp;(' + sizestr.substring(0, len) + sizestr.substr(len + 3, 2) + ')';
    }
    return '&nbsp;&nbsp;(' + sizestr + ')';
}

// 删除文件
$(document).on('click', '.icon-remove', function (event) {
    let ind = $(this).parent().index();
    $(this).parent().remove();
    fileLists.splice(ind, 1);
    fileNameList.splice(ind, 1);
    console.log(fileLists)
});

下面是前端提交form表单数据(包含文件)到后台的js,也就是django模板加载的addDataStructure.js

// 导入上传附加的js
document.write('<script src="../../../../static/public/js/fileUpload.js" type="text/javascript" charset="utf-8"></script>');

function SubmitForm() {
    var submitbtn1 = document.getElementById("SubmitTop");
    var submitbtn2 = document.getElementById("SubmitBottom");
    var title = document.getElementById("id_title").value;
    var full_name = document.getElementById("id_full_name").value;
    var knowledge_category = document.getElementById("id_knowledge_category").value;
    var team = document.getElementById("id_team").value;
    console.log(title, full_name, knowledge_category, team)
    //确认必选项是否都已填
    if (title === '' || full_name === '' || knowledge_category === '' || team === '') {
        submitbtn2.disabled = false;
        submitbtn1.disabled = false;
        $.messager.alert("提示", 'Must input all items with * !', "warning");
        return false;
    }
    var formData = new FormData($("#dataStructureForm")[0]);
    if (fileLists.length > 0) {
        var fileSize = 0
        // 判断文件大小
        for (let i = 0; i < fileLists.length; i++) {
            fileSize += fileLists[i].size
            if (fileSize > 50 * 1024 * 1024) {
                console.log('file size:', sizeToStr(fileSize))
                $.messager.alert("提示", '所有文件大小不能超过50M!', "warning");
                return
            }
        }
        // 将文件添加到form表单中
        $(fileLists).each(function (i, e) {
            formData.append('uploadFile', e)
        })
    }
    submit(formData)
}

function submit(formData) {
    var submitbtn1 = document.getElementById("SubmitTop");
    var submitbtn2 = document.getElementById("SubmitBottom");
    submitbtn2.disabled = true;
    submitbtn1.disabled = true;
    submitbtn1.value = "loading...";
    submitbtn2.value = "loading...";
    $.ajax({
        url: server_url + '/teams/add_data_structure/',
        method: 'POST',
        data: formData,
        dataType: "json",
        processData: false,
        contentType: false,
        cache: false,
        success: function (data) {
            console.log("data:" + data);
            console.log("data:" + data.res);
            if (data.status === 200) {
                $.messager.alert("提示", data.msg, "info");
                console.log("data:" + data.msg);
                window.setTimeout("window.location=server_url+'/teams/data_structure'", 500);
                return;
            }
            submitbtn2.disabled = false;
            submitbtn1.disabled = false;
            submitbtn1.value = "Submit";
            submitbtn2.value = "Submit";
            console.log(data)
            $.messager.alert("提示", data.msg, "info");
        },
        //请求失败,包含具体的错误信息
        error: function (data) {
            console.log(data.msg);
        }
    });
}

3.django后端

附上django后台处理上传文件的接口代码,仅供参考,这里用的是序列化器写的。
接口url

urlpatterns = [
    path('add_data_structure/', AddDataStructureView.as_view(), name='add_data_structure'),
]

视图函数view.py

class AddDataStructureView(APIView):
    def get(self, request):
        ds = DataStructure()
        knowledge_category = [i[1] for i in ds.CATEGORY_CHOICES]
        teams = [i[1] for i in ds.TEAM_CHOICES]
        context = {
            'add_knowledge_category': knowledge_category,
            'add_teams': teams,
        }
        return render(request, 'teams/data_structure/add_data_structure.html', context)

    @transaction.atomic
    def post(self, request):
        # print(request.data)
        serializer = DataStructureSerializer(data=request.data)
        if serializer.is_valid():
            # serializer.
            ds = serializer.save()
            # handle file
            files_obj = request.FILES.getlist('uploadFile')
            if files_obj:
                handle_files(request, files_obj, ds, TeamFileSerializer)
            return api_success('信息保存成功!Data loading')
        return api_bad_request('表单数据输入有误,认证失败,数据无法保存!')
        

def handle_files(request, files_obj, obj, FileSerializer):
    """
    封装好的文件处理函数,更新和添加都可调用
    """

    files = []
    for file_obj in files_obj:
        filename = file_obj.name
        suffix = filename.rsplit(".", 1)[1]
        file_data = {
            'file': file_obj,
            'filename': filename,
            'suffix': suffix,
        }
        if request.method == 'PUT':
            # 1.本地文件删除
            obj_files = obj.attachments.all()
            for file in obj_files:
                file.file.delete()
            # 2.文件数据记录删除(先删除子表数据记录)
            obj_files.delete()
        fs = FileSerializer(data=file_data)
        if fs.is_valid():
            new_file = fs.save()
            new_file.content_object = obj
            new_file.save()
        files.append(fs.data.get('id'))

    if request.method == 'POST':
        obj.attachments.add(*files)
    if request.method == 'PUT':
        obj.attachments.set(files)

ok,至此,文件的多次累加上传功能完成,文件的编辑回显会在单独发文介绍,若是该文对你有帮助,还望可以点赞收藏加关注哦!3Q!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/171544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库mysql调优

问题描述: mysql dba在mysql服务端启用了连接在空闲一定时间 (10分钟) 后&#xff0c;就自动关闭连接(连接失效)的功能&#xff0c;导致java端连接池在空闲一段时间后&#xff0c;连接被自动关闭(自动失效)。为了避免这种情况出现&#xff0c;可以在dbcp上配置空闲的时候检测连…

PCI、PCI-X、PCI-E、PCI-E Card、Mini PCI-E、M.2、Add-in Card 它们有啥区别?这些概念你搞清楚了吗?

搞硬件或通信的“攻城狮”们&#xff0c;免不了要和各种通信协议及接口打交道。比如&#xff0c;我们经常接触PCI、PCI-X、PCI-E、PCI-E Card、Mini PCI-E、M.2(NGFF)、Add-in Card这些概念&#xff0c;作为“攻城狮”队伍中的一员&#xff0c;你搞清楚它们之间的关系了吗&…

Python爬虫教你爬取csdn作者排行榜

(一)两种爬取方式介绍 1.自动化测试工具 安装好驱动(以前的selenium文章有教程),然后进行元素定位&#xff0c;最后数据提取&#xff0c;用xls表格进行持久化存储 2.requests库 利用基本方法发起请求&#xff0c;获得json数据进行持久化存储 本篇文章先讲解第二种&#xf…

洛谷——树与图dp与状压dp

文章目录[NOIP1996 提高组] 挖地雷题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码最大食物链计数题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码[ZJOI2006]三色二叉树题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #…

浅说 INSERT/块参照/BlockReference

在AutoCAD中使用INSERT命令插入一个块时&#xff0c;生成块参照实体&#xff0c;也叫INSERT实体&#xff0c;在ObjectARX中&#xff0c;对应类AcDbBlockReference。反正就这么一个东西&#xff0c;名儿还挺多&#xff0c;我家乡有句话叫“二嫚她婆婆俩名”&#xff0c;莫非这是…

数据结构(5) treap

活动 - AcWing 参考—《算法竞赛进阶指南》-lyd 目录 一、概述 二、具体操作详解 1.常见操作 2.结构定义 3.操作基础函数 &#xff08;1&#xff09;pushup &#xff08;2&#xff09; 获得一个新节点 &#xff08;3&#xff09;左旋右旋 &#xff08;4&#xff09;建…

centos上面用docker 安装nacos 1.4.1

方式一&#xff1a; 下载nacos本地文件 1. 去官网下载GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.2.上传到Linux服务器中cd /usr/uploadtar -zxvf nacos-serv…

二、QML工程之初始工程代码分析

接着上一讲&#xff0c;当建立完工程之后&#xff0c;IDE 会呈现如下的界面下面的代码是main.cpp&#xff0c;工程起始运行的代码段&#xff0c;具体的函数说明都在代码段里面进行了标注。#include <QGuiApplication> //主要是ui进程运行头函数&#xff0c;包含事件循环&…

【云原生】k8s图形化管理工具之rancher

内容预知 前言 1. Rancher的相关知识 1.1 Rancher的简介 1.2 Rancher与k8s的关系及区别 1.3 Rancher具有的优势 2. Rancher的安装部署 2.1 实验环境与部署图分配 2.2 具体的部署操作 &#xff08;1&#xff09;在 rancher 节点下载 rancher 镜像 &#xff08;2&#xff…

【Spring(七)】详细了解Spring的核心容器

文章目录前言容器总结前言 前面我们完成bean以及依赖注入的相关学习&#xff0c;现在我们进入到核心容器的最后一块内容了&#xff0c;也就是与容器相关操作的学习&#x1f388;&#x1f388;。 容器 前边我们获取容器是这样获取的&#x1f447;&#x1f447;。 这只是获取容器…

Vue CLI脚手架

1、Vue的开发模式 目前我们使用vue的过程都是在html文件中&#xff0c;通过template编写自己的模板、脚本逻辑、样式等。 但是随着项目越来越复杂&#xff0c;我们会采用组件化的方式来进行开发&#xff1a; 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等&#xff…

凌恩生物报告升级,科研美图助力群落互作机制研究

2022年&#xff0c;在多位技术支持与生信工程师的通力合作下凌恩生物报告升级重磅升级&#xff01;扩增子分析流程项目从60到120&#xff0c;可谓是加量不加价的超值更新了&#xff01;结题报告的结果图片可直接用于文章发表&#xff0c;快一起来看看&#xff01;小小的举个例子…

pytorch【Conv2d参数介绍】

def __init__(self,in_channels: int,out_channels: int,kernel_size: _size_2_t,stride: _size_2_t 1,padding: _size_2_t 0,dilation: _size_2_t 1,groups: int 1,bias: bool True,padding_mode: str zeros # TODO: refine this type):in_channels&#xff1a;网络输入…

《Linux性能优化实战》学习笔记 Day03

04 | 零拷贝&#xff1a;如何高效地传输文件&#xff1f; 原文摘抄 文件拷贝 上下文切换的成本并不小&#xff0c;虽然一次切换仅消耗几十纳秒到几微秒&#xff0c;但高并发服务会放大这类时间的消耗。 每周期中的 4 次内存拷贝&#xff0c;其中与物理设备相关的 2 次拷贝是…

再学C语言37:函数、数组和指针

编写一个对数组进行操作的函数&#xff0c;并进行调用 一般形式举例&#xff1a; int sum(int * arr, int n); // 函数原型// 第一个参数把数组地址和类型信息传递给函数// 第二个参数把数组中元素的个数传递给函数int main(void) {...int total;total sum(array_t, 10);...…

分分钟你也能写一个自己需要的 Chrome 扩展程序

废话 对于chrome浏览器想必大家大不陌生了&#xff0c;里面的扩展程序&#xff08;本文后面称插件&#xff09;&#xff0c;多多少少也都用过吧&#xff0c;毕竟可以辅助自己的日常工作&#xff0c;娱乐等等&#xff0c;添加完之后呢&#xff0c;就会多出一些操作按钮&#xf…

年度总结-你觉得什么叫生活?

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。&#x1f468;‍&#x1f4bb;上一篇文章…

Linux常用命令——tr命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) tr 将字符进行替换压缩和删除 补充说明 tr命令可以对来自标准输入的字符进行替换、压缩和删除。它可以将一组字符变成另一组字符&#xff0c;经常用来编写优美的单行命令&#xff0c;作用很强大。 语法 tr(选…

云逗猫——直播弹幕控制机械臂逗猫棒

这个东西思路是很简单的。简单来说在直播画面用文字跟观看者约定一些弹幕指令&#xff0c;然后用爬虫爬取直播弹幕&#xff0c;当爬到弹幕是约定的指令时&#xff0c;通过串口给舵机控制板发送信号&#xff0c;控制板控制舵机转动&#xff0c;机械臂就会做相应的动作。 猫 两…

Lesson2无头单向非循环链表(中)

1.链表 1.1链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。 1.2链表的分类 实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 1.…