SpringBoot + thymeleaf 实现文件上传与下载
SpringBoot + thymeleaf 实现文件上传与下载
·
1、pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.coffee</groupId>
<artifactId>file-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>file-demo</name>
<properties>
<java.version>1.8</java.version>
<commons-fileupload.version>1.4</commons-fileupload.version>
<commons-io.version>2.11.0</commons-io.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- 启用 -->
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、application.properties
server.port=8080
spring.thymeleaf.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.servlet.multipart.enabled=true
# Single file max size
spring.servlet.multipart.max-file-size=1024MB
# All files max size
spring.servlet.multipart.max-request-size=2048MB
3、主启动类
@SpringBootApplication
public class FileDemoApplication {
public static void main(String[] args) {
SpringApplication.run(FileDemoApplication.class, args);
}
}
4、自定义线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class ThreadPoolConfig {
private static final Integer CPU_NUM = Runtime.getRuntime().availableProcessors();
@Bean
public ThreadPoolExecutor customizeThreadPoolExecutor() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CPU_NUM,
CPU_NUM * 2,
5L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(CPU_NUM * 4),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
threadPoolExecutor.allowCoreThreadTimeOut(true);
return threadPoolExecutor;
}
}
5、controller
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author lenovo
*/
@Controller
public class FileController {
private static final String FIREFOX = "Firefox";
private final ThreadPoolExecutor poolExecutor;
public FileController(ThreadPoolExecutor threadPoolExecutor) {
this.poolExecutor = threadPoolExecutor;
}
@PostMapping("/fileUpload")
public ResponseEntity<String> uploadFiles(MultipartFile[] files) throws IOException {
if (files.length > 0) {
String parentFilePath = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
// 定义上传文件的存储路径
String path = "D:" + File.separator + parentFilePath;
File filePath = new File(path);
boolean folderExisted = filePath.exists() || filePath.mkdirs();
if (!folderExisted) {
throw new IOException("Unable to create path");
}
Arrays.asList(files).forEach(file -> poolExecutor.execute(() -> {
try {
file.transferTo(new File(path + File.separator + file.getOriginalFilename()));
} catch (IOException e) {
e.printStackTrace();
}
}));
} else {
return new ResponseEntity<>("没有文件可上传", HttpStatus.OK);
}
return new ResponseEntity<>("文件上传成功", HttpStatus.OK);
}
@GetMapping("/download")
public void download(@RequestParam("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) {
try {
String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files";
//获取文件输入流
File file = new File(realPath, fileName);
FileInputStream inputStream = new FileInputStream(file);
//相应输出流
ServletOutputStream outputStream = response.getOutputStream();
// 判断是否是火狐浏览器
String agent = request.getHeader(HttpHeaders.USER_AGENT);
if (agent.contains(FIREFOX)) {
fileName = new BASE64Encoder().encode(fileName.getBytes(StandardCharsets.UTF_8));
} else {
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
}
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
// Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
// attachment表示以附件方式下载 inline表示在线打开
// filename表示文件的默认名称,因为网络传输只支持URL编码,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
// 告知浏览器文件的大小
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()));
IOUtils.copy(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
6、前端页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 引入jquery css&js -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<!-- 引入bootstrap css&js -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</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="customFile1" name="file">
<label class="custom-file-label" for="customFile1" 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">
<span id="alertMsg"></span>
<button id="closeButton" type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
</div>
<!-- upload.js -->
<script>
// 当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);
});
$("#closeButton").click(function (){
$("#alertDiv1").style.visibility="hidden";
});
// 提交按钮被点击后执行
$("#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: '/fileUploads',
type: 'POST',
data: formData,
cache: false,
contentType: false,
processData: false,
xhr: function () {
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) {
// 提交前重置提示消息为空,并重置进度条
$('#alertMsg1').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;
});
</script>
</body>
</html>
更多推荐
已为社区贡献2条内容
所有评论(0)