那些年我们上传的文件

  • ​​那些年我们上传的文件​​
  • ​​图片的上传​​
  • ​​后端控制文件类型​​
  • ​​文件的显示​​
  • ​​进度条的实现​​
  • ​​文件的下载​​

那些年我们上传的文件

前言:

用了那么多年的电脑,访问了那么多的网站,注册过那么多的账号,发表过那么多帖子,逛了那么多论坛…你有想过一个问题吗?你在上传文件的时候都上传到哪里去了?

百度识图—上传图片

[解密贴]那些年我们上传的文件和下载的文件_java

QQ空间–上传视频

[解密贴]那些年我们上传的文件和下载的文件_tomcat_02


查看源码找了一下,差不多就是这个,视频被传到了这个地址上,当然这是一个servlet处理地址;

[解密贴]那些年我们上传的文件和下载的文件_tomcat_03

处理结束后访问的网络地址-----这是封面图地址

http://m.qpic.cn/psc?/3237c5ef-b287-433a-aef8-8d089c2a198e/ruAMsa53pVQWN7FLK88i5tQ*nXXR08.8qgqrdzObu56d8SGR0BNfewynzKJUdIOApeH5oU4OPGqup.PQ9VPVsY7oHFmCa33uwE48CdNOfpk!/mnull&bo=FAc4BAAAAAABBw8!&rf=photolist&t=5

[解密贴]那些年我们上传的文件和下载的文件_tomcat_04


快7000次请求—这是因为传的是一个视频,上传的文件很大,没有任何一个请求参数可以携带这么大的参数,想想如果我们上传10g的电影,那么该参数就会非常大,服务器可以下接收不到这么大的数据,所以只能及逆行拆分,拆分的过程中服务提供商会对数据进行一定的处理;

比如刚刚我只是传了一个30M(本地),上传后经过TX压缩成了17M,站应用空间直接减半;

在请求的过程中数据以二进制数据进行传输,如果数据过大会进行拆分,所以我们在传数据的时候会看到进度条的原理就是因为这个;

那这个过程是怎样实现的呢? 说一个简单点的—上传图片

图片的上传

这里模拟一个注册页面来实现图片的上传,前端简化页面如下

[解密贴]那些年我们上传的文件和下载的文件_java_05

基本思路是这样的 点击上传,然后显示图片缩略图,最后提交将剩余信息一起提交,所以上传时触发的是异步请求;

第一步----前端页面
要知道图片上传到哪里了,就要搞明白上传图片时发生了什么,

<%--
Created by IntelliJ IDEA.
User: Gavin
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>

</head>


<body>
<form method="post" action="addPlayer.do">

账号:<input type="text" name="name" id="name" placeholder="请输入账号"/>
<dev>${Info}</dev><!--检查信息-->
<br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/>
<dev>${Info}</dev>
<br>
昵称:<input type="text" name="nickname" placeholder="请输入昵称"><br>
头像:<input type="file" id="upFile">
<a href="javascript:void(0)" id="uploadFile" onclick="uploadPic()">立即上传</a>
<br>
<div id="divimg" style="width: 300px;height: 200px">
<img src="${pageContext.request.contextPath}/static/img/jianjiacangcang.jpeg"/>
</div>
<input type="submit" value="注册"/>
</form>
<script type="text/javascript">
function uploadPic() {
console.log($("#upFile")[0]);

}
</script>

</body>
</html>

前端调试可以看到,传输时是以json格式进行的

[解密贴]那些年我们上传的文件和下载的文件_spring_06


由于本次只是上传了一个文件,如果是多个文件还要区分一下,所以取第一个 $("#upFile")[0];

上传时的地址----“C:\fakepath\zjy.jpg”

[解密贴]那些年我们上传的文件和下载的文件_spring_07


好了知道了文件源,下面要知道将文件发送给到哪里,

那么我们上传时的文件在哪里呢?

在该前端对象中找到files属性,

[解密贴]那些年我们上传的文件和下载的文件_java_08


由前端调试器可以看到我们传数据的时候其实是被封装为一个file数组,我们上传的数据就在这个数组里,那我们在后台保存数据的数据就要从这个数组里面去取了,由于只有一个文件,这就不去遍历了,直接取出来就好了

$("#upFile")[0].files[0];

[解密贴]那些年我们上传的文件和下载的文件_java_09

如果上传一个别的格式的,这时候file的type显示为application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,我们可以通过这个来限制上传的文件格式;

[解密贴]那些年我们上传的文件和下载的文件_tomcat_10


别的不说,我们现在只追求最简单的一个功能实现,我们在后端只需要将这个对象解析出来就可以保存到相应位置即可;

不过,在解析之前我们先看一下该对象包含哪些属性—

{
"name":
"zjy.jpg",
"webkitRelativePath":"",
"lastModified":"1640314679842",
"size": 167763,
"type":"image/jpeg"
}

[解密贴]那些年我们上传的文件和下载的文件_java_11


根据属性做一些筛选;

<%--
Created by IntelliJ IDEA.
User: Gavin
Date: 2021/12/20
Time: 19:46
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>

</head>


<body>
<form method="post" action="addPlayer.do">

账号:<input type="text" name="name" id="name" placeholder="请输入账号"/>
<dev>${Info}</dev><!--检查信息-->
<br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/>
<dev>${Info}</dev>
<br>
昵称:<input type="text" name="nickname" placeholder="请输入昵称"><br>
头像:<input type="file" id="upFile">
<a href="javascript:void(0)" id="uploadFile" onclick="uploadPic()">立即上传</a>
<br>
<div id="divimg" style="width: 300px;height: 200px">
<img src="${pageContext.request.contextPath}/static/img/jianjiacangcang.jpeg"/>
</div>
<input type="submit" value="注册"/>
</form>
<script type="text/javascript">
function uploadPic() {
var photoFile = $("#upFile")[0].files[0];

if (photoFile == undefined) {
alert("请上传文件!");
return;
}
var photoType = $("#upFile")[0].files[0].type;
var photoSize = $("#upFile")[0].files[0].size;
if ("image/jpeg" != photoType) {
alert("请上传图片!!!");
return;
}
if (photoSize > 160000) {
alert("上传图片大小不得高于160K!!!");
return;
}

var photo = null;
}
</script>

</body>
</html>

筛选之后要进行提交了,把数据提交给谁,提交到哪里是接下来要做的事;

将文件包装到FormData对象中

var formData = new FormData();
formData.append("headerPic", photoFile);
$.ajax({
url: "${pageContext.request.contextPath}/fileUpload.do",
data: formData,
type: "post",
processData: false,
contentType: false,
dataType: "json",
success: function (data) {
console.log(data);
}
}
)
}

后端接收数据要用MultipertFile 对象

@Controller
public class FileController {

@RequestMapping("/fileUpload.do")
@ResponseBody
public String upDataPic(MultipartFile headerPic){//用MultipartFile接收数据
return "success";
}
}

但是spring并不能将json对象那个直接转换为 MutipartFile对象
,因此我们需要做一下配置----->>文件上传组件
这个id值必须为这个名字multipartResolver,通过该id找到该对象;

<!--    配置文件上传组件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

在配置jar包时遇到了以下小麻烦,具体过程查看以下链接,说不定有你遇到的问题解决思路哦!
​链接地址在这里Error creating bean with name ‘multipartResolver

[解密贴]那些年我们上传的文件和下载的文件_java_12

[解密贴]那些年我们上传的文件和下载的文件_java_13

下面就是要做将文件展示在前端页面上,但是这会面临一个问题,服务器随意访问本地磁盘的文件是不太现实的,也不安全,我们需要将上传的文件放到静态资源项目静态资源文件夹下
右键项目静态文件夹,copy path的到静态资源的地址,
然后修改前端jsp和后端上传路径

[解密贴]那些年我们上传的文件和下载的文件_java_14


[解密贴]那些年我们上传的文件和下载的文件_spring_15


[解密贴]那些年我们上传的文件和下载的文件_tomcat_16


测试---->>

[解密贴]那些年我们上传的文件和下载的文件_java_17

之后要做的就是美化了----设置图片大小尺寸为固定的大小,就可以了

[解密贴]那些年我们上传的文件和下载的文件_java-ee_18

最后做一下整合,单独建立一个文件夹用于存放上传的图片

后端---->>

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

@Controller
public class FileController {

@RequestMapping("/fileUpload.do")
@ResponseBody
public String upDataPic(MultipartFile headerPic, HttpServletRequest request) throws IOException {//用MultipartFile接收数据
//通过request对象获得绝对路径
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/uploadPic");
// 指定文件存储目录
//File dir=new File("C:"+File.separator+"imgs");
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();
}
//获取文件名
String originalFilename = headerPic.getOriginalFilename();
// 文件存储位置
File file = new File(dir, originalFilename);
// 文件保存
headerPic.transferTo(file);
return "success";
}
}

前端—>>

<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
</head>
<body>
<form method="post" action="addPlayer.do">

账号:<input type="text" name="name" id="name" placeholder="请输入账号"/>
<dev>${Info}</dev><!--检查信息-->
<br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/>
<dev>${Info}</dev>
<br>
昵称:<input type="text" name="nickname" placeholder="请输入昵称"><br>
头像:<input type="file" id="upFile">
<a href="javascript:void(0)" id="uploadFile" onclick="uploadPic()">立即上传</a>
<br>
<div id="divimg" style="width: 150px;height: 200px">
<img id="img" src="${pageContext.request.contextPath}/uploadPic/zzy.jpg"/>
</div>
<input type="submit" value="注册"/>
</form>
<script type="text/javascript">
function uploadPic() {
console.log($("#upFile"));
console.log($("#upFile")[0].files[0]);
console.log($("#upFile")[0].files[0].type);
var photoFile = $("#upFile")[0].files[0];
if (photoFile == undefined) { alert("请上传文件!");
return;
}
var photoType = $("#upFile")[0].files[0].type;
var photoSize = $("#upFile")[0].files[0].size;
var photoName= $("#upFile")[0].files[0].name;

if ("image/jpeg" != photoType && "image/png" !=photoType) {
alert("请上传图片!!!");
return;
}
if (photoSize > 1600000) {
alert("上传图片大小不得高于1600K!!!");
return;
}
// $("#img").src="D:\\Program Data\\idea2019Data\\SompleDemo\\myweb2\\target\\myweb2-1.0-SNAPSHOT\\static\\img"+$('#upFile')[0].files[0].name;
var formData = new FormData();
formData.append("headerPic", photoFile);
$.ajax({
url: "${pageContext.request.contextPath}/fileUpload.do",
data: formData,
type: "post",
processData: false,
contentType: false,
// dataType: "json",
success: function (data) {
console.log(data);
window.location.reload();
}
}
)
}
</script>
</body>
</html>

还没结束呢,有很多问题需要解决----比如上传的文件名是一样的,但是文件内容不一样,这样只会存在同名文件中的一个是被替换掉呢还是被覆盖掉?

答案是被覆盖掉

那怎么避免这种情况呢?
前端用户上传我们无法控制,只能上传之后统统改名,这样就不会出现这样重名被覆盖掉的情况;

后端处理----->

使得上传的数据具有唯一性,那么可以通过UUID来实现,

[解密贴]那些年我们上传的文件和下载的文件_spring_19


UUID是一个128bit的数值,是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成的一个128bit的数;

jdk中已经为我们提供了相应的类去实现这个功能;
​​​UUID的详细API出门左转​

简单测试一下

public class TestJson {

public static void main(String[] args){ System.out.println(objectMapper.writeValueAsString(user));
testUUID();
}
public static void testUUID(){
UUID uuid= UUID.randomUUID();
String s = uuid.toString();
System.out.println("UUID:"+s);
String STR="ZZY";
String STR2="ZZY";

byte[] bytes = STR.getBytes();
byte[] bytess = STR2.getBytes();
UUID uuid1 = UUID.nameUUIDFromBytes(bytes);
UUID uuid2= UUID.nameUUIDFromBytes(bytess);
System.out.println(uuid1);
System.out.println(uuid2);
}
}
UUID:993517f1-4467-4d69-b487-a82f4cd5f934
e34775e9-cc05-33b5-8fd9-e075741a4fe7
e34775e9-cc05-33b5-8fd9-e075741a4fe7

可以看到如果调根据名字生成的UUID还是会出现重复,所以只能选择随机生成UUID

后端是这样处理的—>
1,生成一个UUID,
2,获得文件的扩展名
3,将生成的uuid+扩展名拼成一个新文件名
4,多次发送同名文件

@Controller
public class FileController {

@RequestMapping("/fileUpload.do")
@ResponseBody
public String upDataPic(MultipartFile headerPic, HttpServletRequest request) throws IOException {
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/uploadPic");

//File dir=new File("C:"+File.separator+"imgs");
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();
}

String originalFilename = headerPic.getOriginalFilename();

String uuid= UUID.randomUUID().toString();

String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));

String newFileName=uuid.concat(extendsName);
File file = new File(dir, newFileName);
headerPic.transferTo(file);
return "success";
}
}

上传成功!

[解密贴]那些年我们上传的文件和下载的文件_tomcat_20

后端控制文件类型

其实在前端我们也可以控制文件类型,通过获得文件的 type属性来实现提醒,后端则是通过获得扩展名来实现文件的上传控制;

那我们来看前端实现了什么功能?

[解密贴]那些年我们上传的文件和下载的文件_java-ee_21


检查文件类型,并返回信息

这里以一个map对象返回

[解密贴]那些年我们上传的文件和下载的文件_上传_22


到这里你会发现之前在前端做判断的话,前端代码很多,容易暴露一些业务逻辑,所以实际工作中将这些判断逻辑放在后端去处理,前端放很少的内容,前端一般是用于展示而非逻辑判断

精简后的前端

<%@ page contentType="text/html;charset=UTF-8" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js">
</script>
</head>
<body>
<form method="post" action="addPlayer.do">
账号:<input type="text" name="name" id="name" placeholder="请输入账号"/>
<dev>${Info}</dev><!--检查信息-->
<br>
密码:<input type="password" name="pwd" id="pwd" placeholder="请输入密码"/>
<dev>${Info}</dev>
<br>
昵称:<input type="text" name="nickname" placeholder="请输入昵称"><br>
头像:<input type="file" id="upFile">
<a href="javascript:void(0)" id="uploadFile" onclick="uploadPic()">立即上传</a>
<br>
<div id="divimg" style="width: 150px;height: 200px">
<img id="img" src="${pageContext.request.contextPath}/uploadPic/zzy.jpg"/>
</div>
<input type="submit" value="注册"/>
</form>

<script type="text/javascript">
function uploadPic() {
var photoFile = $("#upFile")[0].files[0];
var formData = new FormData();
formData.append("headerPic", photoFile);
$.ajax({
url: "${pageContext.request.contextPath}/fileUpload.do",
data: formData,
type: "post",
processData: false,
contentType: false,
// dataType: "json",
success: function (data) {
console.log(data);
alert(data.msg);
window.location.reload();
}
}
)
}
</script>
</body>
</html>

后端----->>

@Controller
public class FileController {
@RequestMapping("/fileUpload.do")
@ResponseBody
public Map<String, String> upDataPic(MultipartFile headerPic, HttpServletRequest request) throws IOException {
Map<String, String> map = new HashMap<>();
//通过request对象获得绝对路径
ServletContext servletContext = request.getServletContext();
String realPath = servletContext.getRealPath("/uploadPic");
// 指定文件存储目录
//File dir=new File("C:"+File.separator+"imgs");
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();
}

if (headerPic==null){
map.put("msg","请上传图片!");
return map;
}
//获取文件名
String originalFilename = headerPic.getOriginalFilename();
// 控制文件大小
if (headerPic.getSize()>1024*1024*1){
map.put("msg","文件大小不超过1M");
return map;
}
// 避免名字冲突,用UUID替换文件名,但是扩展名不变
String uuid = UUID.randomUUID().toString();
// 获取拓展名
String extendsName = originalFilename.substring(originalFilename.lastIndexOf("."));
if (!(".jpg".equals(extendsName) || ".png".equals(extendsName))) {
map.put("msg", "文件类型不符合要求");
return map;
}
//拼接成新名字
String newFileName = uuid.concat(extendsName);
// 文件存储位置
File file = new File(dir, newFileName);
// 文件保存
headerPic.transferTo(file);
map.put("msg","文件上传成功");
// 返回新生成的文件名
map.put("newFileName",newFileName);
map.put("fileType",headerPic.getContentType());
return map;
}
}

除了在java代码中限制i文件大小,还可以通过配置上传时最大的文件大小来实现这个目的

<!--    配置文件上传组件-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1048576"></property>
</bean>

这个配置文件一般是配合后端java代码来更好地约束文件的大小;

为什么不建议单独使用这个配置文件来实现文件大小的约束呢?

首先单独使用这种方式,前端用户无法收到提示信息,其次如果文件大小不符合要求,请求时会出现 500错误

[解密贴]那些年我们上传的文件和下载的文件_java_23


同时后端代码会出现异常

在路径为/myweb2_war_exploded的上下文中,Servlet[myDeparture]的Servlet.service()引发了具有根本原因的异常Request processing failed; nested exception is org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 1048576 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1169635) exceeds the configured maximum (1048576)
org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1169635) exceeds the configured maximum (1048576)

可以看到QQ在上传文件时会有一个缩略图显示,它是怎么实现的呢?

文件的显示

这个实现起来就就比较简单了,加一个img标签,添加src,不过这个是动态实现的;
后端不需要做修改,只需要将前端收到的文件名塞到img标签下的src属性上

前端代码---->>改动部分

[解密贴]那些年我们上传的文件和下载的文件_tomcat_24

[解密贴]那些年我们上传的文件和下载的文件_tomcat_25

进度条的实现

一直很好奇进度条的实现,代码怎么实现?
用到的工具---------->>
准备材料:
1,准备一个文件
2,通过FormData发送对象----ajax中传输二进制文件的一个de接口;
3,通过jquery中的XmlHttpRequest.upload来监听文件上传进度.,这个方法会返回一个upload对象,

前端页面
准备一个进度条
其实就是两个嵌套的div来实现的
外层div是

<!--    给进度条君添加一些样式-->
<style>
.progress {
width: 400px;
height: 10px;
border-radius: 10px;
margin: 10px 0px;
overflow: hidden;
}

.progress > div {
width: 0px;
height: 100px;
background-color: yellowgreen;
transition: all .3s ease;
}
</style>
<div class="progress">
<div>
</div>
</div>

然后就要通过异步ajax来实现进度条

function uploadPic() {
var photoFile = $("#upFile")[0].files[0];
var formData = new FormData();
formData.append("headerPic", photoFile);
$.ajax({
url: "${pageContext.request.contextPath}/fileUpload.do",
data: formData,
type: "post",
processData: false,
contentType: false,
// dataType: "json",
success: function (data) {
console.log(data);
alert(data.msg);
$("#img").attr("src", "uploadPic/" + data.newFileName);
//window.location.reload();
},
xhr: function () {
var xhr = new XMLHttpRequest();
//使用XMLHttpRequest.upload监听上传过程,注册progress事件,打印回调函数中的event事件
xhr.upload.addEventListener('progress', function (e) {
console.log(e);
//loaded代表上传了多少
//total代表总数为多少
var progressRate = (e.loaded / e.total) * 100 + '%';
$("#divNum").text(progressRate);//显示百分比
//通过设置进度条的宽度达到效果
$('.progress > div').css('width', progressRate);
})
return xhr;//每次监听都返回一个xhr
}
}
);

}

简易效果如下—>

[解密贴]那些年我们上传的文件和下载的文件_tomcat_26

文件的下载

那时候对云盘还没有什么概念,只知道邮箱里有一个中转站,算是早期网盘的雏形;

[解密贴]那些年我们上传的文件和下载的文件_java-ee_27


大约在2014年的时候网盘才浮现在我的视野里,到现在用过好多云盘,360云盘,百度云盘,新浪微盘,阿里云盘,腾讯微盘,现在还活着的…

说远了,关于文件的下载,跟文件的上传是反着来的,可以理解位服务器上传文件到用户计算机;

前端案例---->>
前端展示要下载的内容,用户点击下载就可以下载到本地;

<%--
Created by IntelliJ IDEA.
User: Gavin
Date: 2022/1/8
Time: 14:56
To change this template use File | Settings | File Templates.
--%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
#playerTable {
width: 50%;
border: 3px solid cadetblue;
margin: 0px auto;
text-align: center;
}

#playerTable th, td {
border: 1px solid gray;
}

#playerTable img {
width: 100px;
height: 100px;
}
</style>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-3.5.1.min.js"></script>
<script>
// 进入这个页面一上来就加载所有信息
$(function () {
$.ajax({
type: "post",
url: "getAllUser",/*获得所有信息*/
success: function (users) {
$.each(users, function (i, e) {
$("#playerTable").append('<tr>\n' +
' <td>' + e.id + '</td>\n' +
' <td>' + e.name + '</td>\n' +
' <td>' + e.pwd + '</td>\n' +
' <td>' + e.nickname + '</td>\n' +
' <td>\n' +
' <img src="http://192.168.1.2:8090/loadPic/' + e.picname + '" alt="" src>\n' +
' </td>\n' +
' <td>\n' +/*点击下载跳转链接,使得文件发送到用户端*/
' <a href="fileDownload.do?picname='+e.picname+'&filetype='+e.filetype+'">下载</a>\n' +
' </td>\n' +
' </tr>')
})
}
})
})
</script>
</head>
<body>
<table id="playerTable" cellspacing="0xp" cellpadding="0px">
<tr>
<th>编号</th>
<th>账号</th>
<th>密码</th>
<th>昵称</th>
<th>头像</th>
<th>下载</th>
</tr>

</table>
</body>
</html>

[解密贴]那些年我们上传的文件和下载的文件_java_28

在这之前准备的数据库的一些其他改动就省略了;
后端页面

/**
* 文件下载
*
* @param picname 文件名
* @param fileType 文件类型--前端
* @param resp 响应对象
* @throws IOException
*/

@RequestMapping("/fileDownload.do")
public void fileDownLoad(String picname, String fileType, HttpServletResponse resp) throws IOException {
//设置响应头
// 这里如果传过来id则还要到数据库查,不如直接穿过来文件名
// 解析文件名----数据库里没有直接存文件格式,所以要在这里解析文件格式,但是这列格式并非前端识别的格式,所以还是修改之前的在用户表上添加前端文件格式

// 保存数据到磁盘上,不在浏览器上直接解析
resp.setHeader("Content-Disposition", "attachment;filename=" + picname);
resp.setContentType(fileType);
// 获取一个文件输入流
InputStream inputStream = new URL(pic_location+picname).openStream();
// 浏览器获得输出文件
ServletOutputStream outputStream = resp.getOutputStream();
IOUtils.copy(inputStream,outputStream);
}
@RequestMapping("/showUser.do")
public String showUser(){

return "/WEB-INF/serviceData/loginPage/picDown.jsp";
}

整个完整的文上传与下载界面---->>

[解密贴]那些年我们上传的文件和下载的文件_tomcat_29