本文使用ajax方法来实现文件上传带进度条显示。进度条样式使用了Bootstrap的progress bar组件。进度条的计算使用了XmlHttpRequest的onprogress事件控制器。
使用到的技术:
- Spring Boot
- Thymelead(前台模板引擎)
- Bootstrap 4.4.1
- JQuery 3.4.1
- Maven,Java SE1.8,Apache Tomcat等
项目结构(红框内为关联文件,yml等基础配置也是需要的)
YML启动文件,注意设置文件上传的大小限制,默认的是10MB。
server:
#端口号设为8082
port: 8082
spring:
mvc:
#静态文件路径
static-path-pattern: /static/**
thymeleaf:
enabled: true
#编码格式
encoding: UTF-8
#模板前缀
prefix: classpath:/templates/
#模板后缀
suffix: .html
servlet:
multipart:
#上传文件的文件大小限制
max-file-size: 200MB
max-request-size: 200MB
前台HTML部分
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="/static/css/style.css">
<!-- 引入jquery css&js -->
<!-- CDN为国外源,如不可访问请自行切换 -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<!-- 引入bootstrap css&js -->
<!-- CDN为国外源,如不可访问请自行切换 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-10 mx-auto">
<form class="mt-5" method="post" enctype="multipart/form-data">
<div class="input-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="customFile" name="file">
<label class="custom-file-label" for="customFile" data-browse="选择文件">点击选择...</label>
</div>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary reset">重选</button>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<div class="text-center">
<button type="submit" id="uploadFileBtn" class="btn btn-outline-secondary">提交</button>
</div>
</div>
</div>
</form>
<!-- Bootstrap Progress bar -->
<div class="progress mt-3">
<div id="progressBar" class="progress-bar progress-bar-success" role="progressbar"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">0%</div>
</div>
<!-- Bootstrap Alert -->
<div id="alertDiv" class="mt-3 alert alert-warning alert-dismissible fade" role="alert">
<font id="alertMsg"></font>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
</div>
<!-- 引入upload.js -->
<script src="/static/js/upload.js"></script>
</body>
</html>
实际显示效果
Javascript部分如下:
// 当input标签文件被置换
$('.custom-file-input').on('change',function(){
$(this).next('.custom-file-label').html($(this)[0].files[0].name);
// 恢复提交按钮
$('button[type=submit]').prop('disabled',false);
});
// 重选文件,重选按钮被点击后执行
$('.reset').click(function(){
$(this).parent().prev().children('.custom-file-label').html('点击选择...');
$('.custom-file-input').val('');
// 恢复提交按钮
$('button[type=submit]').prop('disabled',false);
});
// 提交按钮被点击后执行
$("#uploadFileBtn").click(function(e){
e.preventDefault();
// 置灰按钮无效,前台防止双重提交
$(this).prop('disabled',true);
// 获取文件
var file = $('#customFile')[0].files[0];
var formData = new FormData();
formData.append("file",file);
$.ajax({
url : '/fileUpload',
type : 'POST',
data : formData,
cache : false,
contentType : false,
processData : false,
xhr: function(){
//Get XmlHttpRequest object
var xhr = $.ajaxSettings.xhr() ;
// 设置onprogress事件控制器
xhr.upload.onprogress = function(event){
var perc = Math.round((event.loaded / event.total) * 100);
$('#progressBar').text(perc + '%');
$('#progressBar').css('width',perc + '%');
};
return xhr ;
},
beforeSend: function( xhr ) {
// 提交前重置提示消息为空,并重置进度条
$('#alertMsg').text('');
$('#progressBar').text('');
$('#progressBar').css('width','0%');
}
})
.done(function(msg){
// 添加提示框显示类
$('#alertDiv').addClass("show");
// 设置返回消息
$('#alertMsg').text(msg);
// 清空文件
$('input[type=file]').val('');
// 恢复提交按钮
$('button[type=submit]').prop('disabled',false);
})
.fail(function(jqXHR){
// 添加提示框显示类
$('#alertDiv').addClass("show");
// 设置返回消息
$('#alertMsg').text("发生错误");
});
return false;
});
Controller中有两个方法,一个为初始化当前上传画面的方法。另一个为接受上传文件并保存到指定目录的方法。
初始化的方法对应的url为http://localhost:8082/upload
接受上传文件的方法对应的url为http://localhost:8082/fileUpload
Controller的接口如下:
package com.example.demo.controller;
import java.io.IOException;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
@Controller
public interface ExampleController {
/*
* 概要:初期化上传画面
* 输入: - -
* 输出: ModelAndView modelAndView
* @throws
*/
@GetMapping(value="/upload")
ModelAndView upload();
/*
* 概要:接受上传文件
* 输入: MultipartFile file
* 输出: ResponseEntity responseEntity
* @throws IOException
*/
@PostMapping(value="/fileUpload")
ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) throws IOException;
}
Controller实现方法如下:
package com.example.demo.controllerImpl;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.example.demo.constant.SystemConstants;
import com.example.demo.controller.ExampleController;
@Component
public class ExampleControllerImpl implements ExampleController{
@Override
public ModelAndView upload() {
// TODO Auto-generated method stub
ModelAndView modelAndView = new ModelAndView();
// 设置视图名称,这里设定了固定变量,变量实际值为"upload-file",由于后缀.html已经在yml文件设定了
// 因此括号内可直接替换为
// modelAndView.setViewName("upload-file");
modelAndView.setViewName(SystemConstants.PAGE_UPLOAD);
return modelAndView;
}
@Override
public ResponseEntity<?> uploadFile(MultipartFile file) throws IOException {
// TODO Auto-generated method stub
// 如果文件不为空
if (!file.getOriginalFilename().isEmpty()) {
BufferedOutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(
// 注意改为自己电脑的路径
new File("C:/Users/zhewen.li/wktechno-test", file.getOriginalFilename())));
outputStream.write(file.getBytes());
outputStream.flush();
outputStream.close();
}else{
return new ResponseEntity<>("空文件或不合法文件。",HttpStatus.BAD_REQUEST);
}
return new ResponseEntity("文件上传成功",HttpStatus.OK);
}
}
存放固定常量的java文件,此存放模板名称的,非必须
package com.example.demo.constant;
public class SystemConstants {
public final static String PAGE_UPLOAD = "upload-file";
}
实际执行效果:
选择视频文件:Autumn_leaves_12_time-lapse.mp4,并点击提交。
确认文件被成功上传