作者:19级学生程渝林
常规的文件下载处理基于JavaWeb
常规的文件上传与下载,就是通过前端页面通过文件类型的表单, 发起请求传输到java后端 (注意表单里的数据类型必须是:enctype="multipart/form-data") 使用这种数据类型会把文件也写入表单,这样才能传入到后台 且前端传入时,携带的传入文件名,必须与后台接收到的一致 (待会以SpringBoot形式基于springmvc进行演示) 然后通过输入输出流方式,写进后台,并进行缓存区大小的设置, 创建文件夹目录,设置传输文件大小,判断文件大小,写入磁盘, 关闭流的一系列操作。
html表单:
<!DOCTYPE html >
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/file/image" method="post" enctype="multipart/form-data">
<input type="file" name="file" value="请选择文件" multiple>
<input type="submit" value="上传">
</form>
</body>
</html>
普通上传下载基于http请求的
@WebServlet(name = "FileUploadServlet",value = "/Upload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doPost");
Part part=request.getPart("load");
String path=request.getServletContext().getRealPath("image");
System.out.println(path);
String fileName=uploadFile(part,path);
System.out.println("new");
PrintWriter out=response.getWriter();
out.println("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"utf-8\">\n" +
" <title>Upload</title>\n" +
"</head>\n"
);
out.println("<img src='image/"+fileName+"'/>");
out.println("</body>\n" +
"</html>");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
public String uploadFile(Part part,String path)throws IOException{
System.out.println(part.getSubmittedFileName());
InputStream inputStream = part.getInputStream();
byte[] bytes = new byte[1024];
int length=0;
System.out.println(path);
File file=new File(path);
if(!file.exists()){
file.mkdir();//创建目录
}
UUID uuid=UUID.randomUUID();
String fileName=uuid.toString()+part.getSubmittedFileName();
OutputStream outputStream=new FileOutputStream(file+"/"+fileName);
while ((length=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,length);
}
outputStream.close();
inputStream.close();
return fileName;
}
}
下载:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doGet");
request.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
String fileName = request.getParameter("file");
response.setHeader("Content-Disposition","attachment;fileName="+fileName);
OutputStream out=response.getOutputStream();
//PrintWriter out=response.getWriter();
String path=this.getServletContext().getRealPath("/images");
File file=new File(path+"\\"+fileName);
if(file.exists()){
InputStream is=new BufferedInputStream(new FileInputStream(file));
int len=0;
byte[] buffer = new byte[200];
while((len=is.read(buffer))!=-1){
out.write(buffer,0,len);
//out.print(new String(buffer,0,len));
}
is.close();
out.close();
}
}
基于SpringBoot的文件上传下载
前端同理,而SpringMvc自己封装了一个MultipartFile,点开MultipartFile的源文件,
MultipartFile是继承于InputStreamSource,
并且该方法返回的是是inputstream类型,那么可以得知,其实MultipartFile就是以流的形式来处理文件的,实质上和刚刚所说的普通的以流的形式处理文件是一样的。具体的代码可以自行查看 那么知道了spring已经为我们封装好了文件处理方式,那么就来处理文件, 在用MultipartFile的对象将文件从前端传到后台,在MultipartFile对象中已经帮我们把文件进行处理过了,所以我们需要做的,就是判断文件是否传入,判断文件大小是否正常,还可以通过文件后辍判断文件是否是你所需要的文件类型,也可以将文件名进行处理,以此避免文件名重复,最后将你的文件传入你所指定的磁盘地址。同时这里也可以自定义创建文件夹保存。
结合刚刚的html代码就可以使用了
基于springmvc的文件下载
//上传到的文件位置---非本地
private String newFileName = null;
private String fileType = null;
@RequestMapping("/upload")
public Map<String, String> FileUpload(@RequestParam(name = "file", required = false)MultipartFile multipartFile) {
Map<String, String> map = new HashMap<>();
//文件不存在
if (multipartFile == null) {
map.put("msg", "请上传文件");
return map;
}
//文件存在,获取文件名,文件地址
String filename = multipartFile.getOriginalFilename();
//控制文件大小
Integer size = 1024 * 1024 * 10;
if (multipartFile.getSize() > size) {
map.put("msg", "文件不能超过10M");
return map;
}
//重命名文件名
String uuid = UUID.randomUUID().toString();
//只支持JPG和PNG格式的文件类型
String[] exName = {".jpg", ".png"};
String ExtendName = filename.substring(filename.lastIndexOf("."));
if(!(exName[0].equals(ExtendName)||exName[1].equals(ExtendName))){
map.put("mag","不支持的文件类型");
return map;
}
//拼接文件名
newFileName=uuid.concat(ExtendName);
String filePath = new File("").getAbsolutePath();
File fileUpload = new File(filePath);
if(!fileUpload.exists()){
fileUpload.mkdirs();
}
fileUpload = new File(filePath,newFileName);
try {
multipartFile.transferTo(fileUpload);
map.put("msg","上传成功");
fileType=multipartFile.getContentType();
map.put("fileType",fileType);
return map;
} catch (IOException e) {
map.put("上传失败",e.toString());
}
return map;
}
为什么不将文件存储在服务器或本机主机,为什么本机主机只进行文件的临时存储与上传下载?
例如有用户传输文件给我,我如何处理这个文件?是将文件直接保存到本地,利用本地路径存储到数据库,等到前端需要展示时再通过其文件路径将文件展示出来吗?这是可以行的,但我认为,将工程和数据存储在一个服务器上是不合理的行为,这样能够减轻工程服务器压力,并且在使用时,本地服务器存储的图片是没有进行压缩的,如果服务器系统出现错误,容易出现数据出错或者丢失,并且大大减少了工程的可扩展性。在最初的我只是觉得以这种方式存储,在拿去文件的时候十分麻烦,并且不易存储。因为我存储图片都是以图床的形式去存储。 于是就有了云存储。
什么是云存储,为什么要使用云存储?好处?
我的理解:在我查阅了资料过后,云存储其实也是服务器存储数据,云存储就是将数据放在云端。当然毕竟是在别人公司的服务器里,相对来说不是绝对安全的,但对于我们学习和开发使用,我觉得是非常不错的。 在这里,我们将文件类的大型数据放在云存储里,减少了我们工程服务器的压力,并且云存储会将图片进行压缩,将图片放在云端,以图床形式返回给我们。 最重要的是,他以图床的形式返回给我们,能以更方便的形式存储在数据库中。 就是专门用来存放图片,同时允许你把图片对外连接的网上空间,实际上是一个链接,访问连接得到图片。 比如: http://baidu.com/6plmqdj5bcr
使用云存储
首先我们要了解云存储的结构,阿里云,腾讯云,七牛云都可以。这里我以七牛云举例。 创建存储空间:在七牛云的控制台中:
存储区域可以选择默认
这里首先提一点,没有域名的就使用他提供的测试域名,有备案域名的,使用自己的 备案域名,域名仅仅是访问图片地址的地址,没有域名的也不要有压力。 打开空间管理=>文件管理=>点击上传文件,上传成功后,会有一个地址,打开地址, 就能看到自己上传的图片了。 那么问题来了,java如何上传图片呢?返回给我的地址如何获取呢? 在这里首先我们要了解一个东西叫http请求,对就是计算机网络教的http请求, 但是在这里我们是需要用java去进行http请求。 然后我们要了解SDK,什么是SDK?就是软件开发工具包,在java的maven仓库里就是我 们的dependency依赖。我使用的是七牛云的云存储,那么相应的要使用七牛云的JAVA SDK
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.10.2</version>
</dependency>
七牛云JavaSDK文档:https://developer.qiniu.com/linking/6279/linking-java-sdk
利用Java对七牛云存储获取token接口
什么是token?
token简单理解就是对七牛云存储上传所需要的密钥也就是上传凭证,深度理解就是通过七牛云空间的用户id和密码生成的base64加密数据,具体的封装过程七牛云的SDK已经为我们封装好。
获取token:
这里了解两个东西: 在七牛云的个人中心=>密钥管理中,有这两个密钥 accessKey secretKey这是每个用户都会有的独特的密码,通过密码获取上传凭证token,当然并不是只有这两个还有你的上传地址。这里上传地址并不是你访问的测试地址,而是你的空间名,注意我最开始截图画的重点,空间名。 我们获取token需要向七牛云服务端获取这几个参数: accessKey secretKey bucket:你的空间名
在有了这三个参数的时候,我们就可以获取token了
代码:
七牛实体类:
@Data
public class QiNiu {
private String token;
private String key;
}
获取token
QiNiu qiNiu = new QiNiu();
// 获取 七牛云的 token
@RequestMapping(value = "/getToken", method = RequestMethod.GET)
public String getToken() {
String accessKey = "自己的accessKey";
String secretKey = "自己的secretKey";
String bucket = "自己的空间名";
long expireSeconds = 600; //过期时间
StringMap putPolicy = new StringMap();
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket,null,expireSeconds,putPolicy);
qiNiu.setKey(UUID.randomUUID().toString().replaceAll("\\-", ""));
qiNiu.setToken(upToken);
return qiNiu.getToken();
}
最后返回String类型的uptoken为了防止token被滥用,我们可以将token进行json格式的封装,用uuid设置上传的指定的key,最后再返回,我这里省的麻烦,就直接拿来用了。 我们已经获取到token了,就可以携带token参数对七牛云存储空间进行文件上传并进行访问了
本地上传文件到七牛
@GetMapping(value = "/qiniu/upload")
public Result UpLoad(){
Result result = new Result();
Configuration cfg = new Configuration(Region.huanan());//Region.huanan()对应自己的存储区域
UploadManager uploadManager = new UploadManager(cfg);
String token=qiNiu.getToken();
String key=qiNiu.getKey();
String url="";//这里是你的域名地址
String filePath="你电脑的图片地址";
try {
Response response = uploadManager.put(filePath,key,token);
DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
System.out.println(putRet.key);
System.out.println(putRet.hash);
String imageUrl=url+putRet.key;
result.setData(imageUrl);
return result;
} catch (QiniuException e) {
e.printStackTrace();
result.setMessage("err");
return result;
}
}
这里返回的key就是我们的文件名,例如滑稽.jpg 所以最后拼接上我们的域名地址就是我们的图片地址了。 当然 我们也可以不用本地文件上传,而是让文件传来的过程中,将文件缓存在服务器,利用流形式,将文件转成二进制,再将文件上传至七牛云,也是可以的。
返回的Result数据类型
package com.school.commons;
import lombok.Data;
/**
* 统一响应结果集
*
*/
@Data
public class Result<T> {
//操作代码
Integer code;
//提示信息
String message;
//结果数据
T data;
public Result() {
}
public Result(ResultCode resultCode) {
this.code = resultCode.code();
this.message = resultCode.message();
}
public Result(ResultCode resultCode, T data) {
this.code = resultCode.code();
this.message = resultCode.message();
this.data = data;
}
public Result(String message) {
this.message = message;
}
public static Result SUCCESS() {
return new Result(ResultCode.SUCCESS);
}
public static <T> Result SUCCESS(T data) {
return new Result(ResultCode.SUCCESS, data);
}
public static Result FAIL() {
return new Result(ResultCode.FAIL);
}
public static Result FAIL(String message) {
return new Result(message);
}
}