目录
不同的框架提供了不同的获取输入流的方式--Servlet上传案例
一、文件的上传和下载的作用
什么是文件上传?
要将客户端(浏览器)大数据存储到服务器端,不将数据直接存储到数据库中,而是要将数据存储到服务器所在的磁盘上,这就要使用文件上传。
文件的下载及需求
广义上,任何从服务器端获取数据的过程都属于文件下载,默认情况下由浏览器打开展示。狭义的文件下载,将从服务器端获取的数据以附件的形式保存到本地,其实就是将服务器端的资源通过io流写回到浏览器端。
文件下载需求:
-
页面显示超链接
-
点击超链接后弹出下载提示框
-
完成图片文件下载
为什么使用文件上传?
通过文件上传,可以将浏览器端的大数据直接保存到服务器端。不将数据保存到数据库中,而是保存到服务器磁盘上,这样减少了数据库服务器的压力,对数据的操作更加灵活
二、文件上传
文件上传对请求的前提:
请求方式:提供
form
表单,method
必须是post
数据类型:表单必须设置
enctype="multipart/form-data"
控件:需要使用控件
< input type="file" name="file">
控件必须有名称
表单编写:
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button>上传</button>
</form>
文件上传的关键
上传文件在服务器上的目录一般已固定,只需要获取了输入流,就可以完成上传任务。上传文件在客户端的路径由用户指定
服务器端处理:
通过request对象,获取InputStream, 可以将浏览器提交的所有数据读取到,解析请求正文的每部分的内容
不同的框架提供了不同的获取输入流的方式--Servlet上传案例
Servlet提供了Part对象获取输入流。下面演示的是将文件上传到D:/upload/目录下保存。
package com.bib.servlet;
import com.bib.utils.IOUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@MultipartConfig
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取上传到服务器的临时文件对象
Part part = req.getPart("file");
//获取上传的文件名
String submittedFileName = part.getSubmittedFileName();
//获取临时文件的输入流
InputStream inputStream = part.getInputStream();
//构建文件输出流
FileOutputStream fileOutputStream = new
FileOutputStream("D:/upload/"+submittedFileName);
//IO流读写操作
IOUtils.readAndWrite(inputStream, fileOutputStream);
//跳回首页
resp.sendRedirect("/index");
}
}
文件上传细节
文件重名问题
每一个客户端都可以进行文件上传操作,那么当我们上传的文件过多,一定会出现同名的文件,那么在服务器端只能保存一个,对于这个问题,我们在上传文件时,就需要考虑文件重名问题
解决方案
一般情况下,对于上传文件,为了保证不重名,会给文件起一个随机名
一种方案是使用uuid
一种方案是使用毫秒值
存储位置
上传操作文件存储位置分析
本质就是上传的文件是否允许浏览器端直接访问。例如:商品添加时需要一个图片,这个图片一定是可以直接被浏览器端访问的。
解决方案
-
允许被浏览器端访问:
放置在Web目录下,但不能是WEB-INF或META-INF下其及子目录下
-
不允许被浏览器端访问:
工程下:放置在WEB-INF或META-INF及其子目录下
文件上传问题-目录分离
上传操作文件过多问题分析
当我们上传文件过多,并且保存在同一个目录下时,我们就需要考虑怎样处理它们,因为一个目录下文件过多,不仅降低性能,操作时也不方便。
解决方案
为了防止同一个目录下方上传文件数量过多
按照上传时间进行目录分离 (周、月 )
按照上传用户进行目录分离 ----- 为每个用户建立单独目录
按照固定数量进行目录分离 ------ 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录
按照唯一文件名的hashcode 进行目录分离
三、文件下载
下载的关键
下载是从服务器固定目录获取文件,输入流可知,输出路径由用户定义,因此关键是创建输出流。
超链接下载问题分析
超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
任何资源都必须弹出下载提示框
使用响应头设置资源的打开方式
content-disposition:attachment;filename=xxx
Servlet下载
Servlet提供了ServletOuputStream作为文件下载的输出流。
package com.bib.servlet;
import com.bib.utils.IOUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
FileInputStream fileInputStream = new FileInputStream("d:/upload/" + name);
int len = fileInputStream.available();
ServletOutputStream outputStream = resp.getOutputStream();
//1.告诉浏览器文件类型是二进制
resp.setContentType("application/octet-stream");
//2.告诉浏览器文件名
resp.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(name,"utf-8"));
//3.告诉浏览器文件大小
resp.setContentLength(len);
//IO流读写操作
IOUtils.readAndWrite(fileInputStream,outputStream);
}
}
四、文件上传及下载演示
IndexServlet.java代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建File对象,指向文件上传的目录
File file = new File("D:/upload");
//获取该目录下的文件列表
String[] list = file.list();
//将文件列表存到request作用域
req.setAttribute("list", list);
//转发到index.jsp
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
}
index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>文件列表</title>
</head>
<body>
<%--文件显示--%>
<h1>文件列表</h1>
<c:forEach items="${list}" var="f">
<a href="download?name=${f}">${f}</a><br>
</c:forEach>
</body>
</html>
运行结果
首先访问upload.jsp页面,进行文件上传
点击选择文件,选择本地的任意文件
点击上传,文件上传至D:/upload/目录下,跳转至index.jsp页面显示
查看D:/upload文件夹
点击文件下载
在下载路径查看文件
至此完成 简单的文件的上传与下载
关于下载时乱码问题
对于下载时,我们在显示下载文件名称时,如果包含了中文,就可能出现乱码问题,出现的原因,是对于不同的浏览器,它们在处理下载文件时的编码不一致,ie浏览器使用的是utf-8编码,而firefox浏览器使用的是base64编码
解决思路:
-
获取客户端使用的浏览器版本信息
- 根据不同的版本信息,设置文件名的编码方式不同
//1.告诉浏览器文件类型是二进制
resp.setContentType("application/octet-stream");
//2.告诉浏览器文件名
resp.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(name,"utf-8"));
//3.告诉浏览器文件大小
resp.setContentLength(len);