需求:可以选择多文件,并展示出来,可以进行前端删除,选择需要的进行上传;下载附件
<h1>多文件选择上传</h1>
<input id="selectInputFile" name="selectInputFiles" multiple="multiple" type="file" style="display:none"/>
<ol>
<li ng-repeat="file in repeatFiles track by $index">
<span>{{file.name}}</span>
<a href="javascript:void(0);" ng-click="deleteRepeatFile($index)">删除</a>
</li>
</ol>
<button class="btn" type="submit" ng-click="selectFile()">选择文件</button>
<button class="btn" type="submit" ng-click="fileUpload()">上传</button>
隐藏一个input type="file" 去选择文件,当点击“选择文件”的时候,去触发
input type="file"的点击事件
//选择文件按钮触发 input type='file' 选择文件
$scope.selectFile = function(){
$("#selectInputFile").click();
};
再监听选择文件的change事件,获取当前选择的文件push到已有的里面,更新
两种方式都可以获取文件的数组,一般情况下,currentTarget
和 this
是一样的。
添加后面选择的文件时,循环push的话,把一个个file加到 newRepeatFile
里面;如果是concat,直接添加一个数组,就会有一个 fileList
在数组里,页面 ng-repeat
的时候,会有一个空的显示在页面。
//监听 input type='file' 的change事件
$("#selectInputFile").on("change",function (e) {
var selectFiles = document.querySelector('input[name="selectInputFiles"]').files;
//或 e.currentTarget获取文件
//var selectFiles2 = e.currentTarget.files;
//使用concat直接添加数组,最后会加一个数组进去,页面循环会有问题
var newRepeatFile = $scope.repeatFiles;
for (var i=0; i<selectFiles.length; i++) {
newRepeatFile.push(selectFiles[i]);
}
$scope.$apply(function () {
$scope.repeatFiles = newRepeatFile;
});
});
删除的点击事件
$scope.deleteRepeatFile = function(index){
$scope.repeatFiles.splice(index,1);
};
上传按钮点击事件
FormData为序列化表以及创建与表单格式相同的数据(当然是用于XHR传输)提供便利。
直接append,同样的key,后面的value会变成一个数组
Angular 默认的 transformRequest
方法会尝试序列化我们的 FormData
对象,因此此处我们使用 angular.identity
函数来覆盖它;另外,angular 在发送 POST 请求的时候使用的默认 Content-Type
是 application/json
,因此此处需要调整为 undefined
,这时浏览器会自动的帮我们设置成 multipart/form-data
的编码方式,同时还会生成一个合适的 boundary
,如果手动设置成 multipart/form-data
的话就不会生成 boundary
字段了。
$scope.fileUpload = function () {
var form = new FormData();
var selectFiles = $scope.repeatFiles;
for (var i=0; i<selectFiles.length; i++) {
form.append("file", selectFiles[i]);
}
var url = 'file/upload';
$http({
method: 'POST',
url: Config.getApiRemoteUrl() + url,
data: form,
headers: {
'Content-Type':undefined
},
transformRequest: angular.identity
}).then(function successCallback(response) {
if(response.status === 200){
layer.msg("success");
}else{
layer.msg("fail");
}
}, function errorCallback(response) {
layer.msg("error");
});
};
后端上传接口,写到本地路径,localUploadDir 定义了一个本地文件夹,根据年月日时分秒新建文件夹存储文件
private static String localUploadDir;
@Value("${local.fileserver.dir:''}")
public void setLocalUploadDir(String uploadDir) {
localUploadDir = uploadDir;
}
@PostMapping("/upload")
public void uploadFile(@RequestParam(value = "file") MultipartFile[] files){
if(files != null && files.length > 0){
for(MultipartFile file : files){
System.out.println("保存完成 : " + saveUploadFile(file));
}
}
}
private String saveUploadFile(MultipartFile multipartFile) {
Calendar now = Calendar.getInstance();
String fileName = multipartFile.getOriginalFilename();
// 设定文件保存的目录,按年月日时分秒创建文件夹
String path = localUploadDir +
now.get(Calendar.YEAR) + "/" +
(now.get(Calendar.MONTH) + 1) + "/" +
now.get(Calendar.DAY_OF_MONTH) + "/" +
now.get(Calendar.HOUR_OF_DAY) + "/" +
now.get(Calendar.MINUTE) + "/" +
now.get(Calendar.SECOND) + "/";
File file = new File(path);
if (!file.exists()){
file.mkdirs();
}
try (
FileOutputStream fileOutputStream = new FileOutputStream(path + fileName);
InputStream inputStream = multipartFile.getInputStream();
) {
if (!multipartFile.isEmpty()) {
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) > 0){
fileOutputStream.write(buffer,0, len);
}
}
}catch (Exception e){
e.printStackTrace();
}
return path + fileName;
}
效果
文件下载使用a标签,直接把url写在里面
<h1>a标签 文件下载</h1>
<a target="_self" id="download" ng-click="fileDownload()">下载</a>
api接口 @RequestMapping("/api/file")
后端下载接口
在服务器设置响应头,告诉浏览器以 utf-8
的编码显示数据,如果不写会出现中文乱码
设置内容类型为 application/octet-stream
即 .* 代表二进制流,不知道下载文件类型
Content-disposition
是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition
其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
文件名为 new String(fileName.getBytes("GB2312"), "ISO8859-1")
ISO8859-1
是页面上数据传输的格式
GB2312
是你java项目格式(根据实际项目变更),目的是为了将中文文件名正确显示在页面上。
防止中文乱码
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) {
String filePath = localUploadDir + "2019/6/6/11/2/28/测试上传1.txt";
String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
response.setCharacterEncoding ("utf-8");
//二进制流
response.setContentType("application/octet-stream");
try (
FileInputStream fileInputStream = new FileInputStream(filePath);
ServletOutputStream servletOutputStream = response.getOutputStream();
){
//附件
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("GB2312"), "ISO8859-1"));
int len;
byte[] buffer = new byte[1024];
while ((len = fileInputStream.read(buffer)) > 0){
servletOutputStream.write(buffer,0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
$scope.fileDownload = function () {
$('#download').attr('href', './api/file/download');
};
效果
application.yml配置文件
server:
servlet:
context-path: /file
port: 8123
spring:
thymeleaf:
cache: false
encoding: UTF-8
servlet:
multipart:
#上传文件最大大小
max-file-size: 100MB
max-request-size: 1000MB
debug: true
local:
fileserver:
dir: D:/uploadfile/
path: /uploadfile/**