前言
原本的多文件上传功能在选择文件时,只能通过同一范围的鼠标框选或者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) + " <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 ' (' + sizestr.substring(0, len) + sizestr.substr(len + 3, 2) + ')';
}
return ' (' + 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!