八、文件上传、下载
文件上传
- form表单,method=post
- encType属性值必须为multipart/form-data
- 在form标签中使用input type=file添加要上传的文件
- 编写服务器代码(Servlet),接收、处理上传的数据
1、演示
<form action="uploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/>
头像:<input type="file" name="photo"/>
<input type="submit" value="上传"/>
</form>
multipart/form-data表示提交的数据,以多段(每一个表单项作为一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
HTTP请求报文如下
POST /09_EL_JSTL/uploadServlet HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 306
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryYBjPBNY57i0B5j0B
boundary表示每段数据的分隔符
----WebKitFormBoundaryYBjPBNY57i0B5j0B 是每次由浏览器都随机生成,就是多段数据间的分界符
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream is = req.getInputStream();
byte[] buffer = new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
System.out.println(new String(buffer,0,len));
}
}
打印台测试输出如下
------WebKitFormBoundaryIuflEP7Y4UBOf8CD
Content-Disposition: form-data; name="username"
SY2106317
------WebKitFormBoundaryIuflEP7Y4UBOf8CD
Content-Disposition: form-data; name="photo"; filename="1.txt"
Content-Type: text/plain
HelloWorld锛侊紒锛侊紒锛?
------WebKitFormBoundaryIuflEP7Y4UBOf8CD--
2、常用API
由于处理流比较繁琐,所以用外部api来协助处理接受的数据
导入commons-fileupload和commons-io两个jar包
常用类:
- ServletFileUpload,用于解析上传的数据
- boolean ServletFileUpload.isMultipartContent(HttpServletRequest request),判断数据是否是多段格式
- public List< FileItem > parseRequest(HttpServletRequest request),解析上传的各段数据为FileItem对象
- boolean FileItem.isFormField(),判断当前表单项是否是普通表单项,还是上传的文件类型(false表示是上传的文件)
- String FileItem.getFieldName(),获取表单项的name属性值
- String FileItem.getString(),获取表单项的值
- String FileItem.getName(),获取上传的文件名
- void FileItem.write(file),将上传的文件写到参数file所指向的硬盘位置
3、练习
Servlet程序:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 先判断上传数据是否是多段数据(只有是多段的数据,才是文件上传的)
if(ServletFileUpload.isMultipartContent(req)){
// 创建FileItemFactory工厂实现类对象
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析数据的工具类ServletFileUpload类对象
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项FileItem
List<FileItem> fileItems = servletFileUpload.parseRequest(req);
for(FileItem fileItem:fileItems){
if(fileItem.isFormField()){
// 普通表单项
System.out.print("表单项name:"+fileItem.getFieldName());
System.out.println(",表单项value:"+fileItem.getString("UTF-8"));
// 参数utf-8,解决中文乱码问题
}else{
// 文件
System.out.print("表单项name:"+fileItem.getFieldName());
String name = fileItem.getName();
System.out.println("上传的文件名:"+name);
File file = new File(name);
fileItem.write(file);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
文件下载
服务器:
-
获取要客户端下载的文件名
-
读取要下载的文件内容
-
在回传前,通过响应头告诉客户端返回的数据类型、以及收到的数据是用于下载
-
把文件内容回传给客户端
演示
在web目录下新建file目录,并放入要下载的文件,例如2.png
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取要下载的文件名
String downloadFileName = "2.png";
// 2. 获取要下载的文件内容(通过ServletContext对象可以获取)
ServletContext servletContext = getServletContext();
// 获取要下载的文件类型
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型:"+mimeType);
// 3. 告诉客户端回传的数据类型、以及收到的数据是用于下载
resp.setContentType(mimeType);
resp.setHeader("Content-Disposition","attachment; filename="+downloadFileName);
// Content-Disposition响应头,表示收到数据的处理方式
// attachment 附件,表示下载使用
// filename= 指定下载的文件名
// url编码时把汉字转换成为%xx%xx的格式,让下载的文件名支持中文
InputStream is = servletContext.getResourceAsStream("/file/" + downloadFileName);
System.out.println("inputStream:"+is);
// /斜杠为服务器解析表示地址为http://ip:port/工程名/ 映射到代码的web目录
// 获取响应的输出流
OutputStream os = resp.getOutputStream();
// 4. 把要下载的文件内容回传给客户端
// 读取输入流中全部数据,复制给输出流,输出给客户端
IOUtils.copy(is,os);
}
Base64编码
下载文件名还可以用Base64编码
public static void main(String[] args) throws Exception {
String str = "我爱你中国";
BASE64Encoder base64Encoder = new BASE64Encoder();
String s = base64Encoder.encodeBuffer(str.getBytes("UTF-8"));
System.out.println("编码后的结果:"+s);
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] bytes = base64Decoder.decodeBuffer(s);
String s1 = new String(bytes, "UTF-8");
System.out.println("解码后的结果是:"+s1);
}
九、其他
BeanUtils
使用第三方工具类BeanUtils可以直接把HttpServletRequest中的数据封装到Bean对象中,为了降低耦合性,也可以直接把Map的数据封装到Bean对象中
需要导入
commons-beanutils
commons-logging
commons-collections
三个jar包
工具类:
public class WebUtils {
public static <T> T copyParamToBean(Map map, T bean){
try {
BeanUtils.populate(bean, map);
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
然后在Servlet程序中,直接调用就可以返回封装好属性值的Bean对象
底层调用了Bean对象的 set属性名(属性值)方法
User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());
MVC模型
MVC:Model模型、View视图、Controller控制器
- View:只负责数据和界面的显示,不接受任何与显示数据无关的代码
- Controller:只负责接收请求,调用业务层的代码处理请求,然后派发页面(转到或重定向到某个页面),是“调度者”——Servlet
- Model:将与业务逻辑相关的数据封装为具体的JavaBean类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo
MVC是一种思想,将软件代码拆分为组件,单独开发,组合使用,可以降低耦合,让代码合理分层