HTML5实现⽂件上传下载功能实例解析
前⾔:因⾃⼰负责的项⽬(jetty内嵌启动的SpringMvc)中需要实现⽂件上传,⽽⾃⼰对java⽂件上传这⼀块未接触过,且对 Http 协议较模糊,故这次采⽤渐进的⽅式来学习⽂件上传的原理与实践。该博客重在实践。
⼀. Http协议原理简介
HTTP是⼀个属于应⽤层的⾯向对象的协议,由于其简捷、快速的⽅式,适⽤于分布式超媒体信息系统。它于1990年提出,经过⼏年的使⽤与发展,得到不断地完善和扩展。⽬前在WWW中使⽤的是HTTP/1.0的第六版,HTTP/1.1的规范化⼯作正在进⾏之中,⽽且HTTP-NG(Next Generation of HTTP)的建议已经提出。
简单来说,就是⼀个基于应⽤层的通信规范:双⽅要进⾏通信,⼤家都要遵守⼀个规范,这个规范就是HTTP协议。
1.特点:
(1)⽀持客户/服务器模式。
(2)简单快速:客户向服务器请求服务时,只需传送请求⽅法和路径。请求⽅法常⽤的有GET、HEAD、POST。每种⽅法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模⼩,因⽽通信速度很快。
(3)灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
(4)⽆连接:⽆连接的含义是限制每次连接只处理⼀个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采⽤这种⽅式可以节省传输时间。
(5)⽆状态:HTTP协议是⽆状态协议。⽆状态是指协议对于事务处理没有记忆能⼒。缺少状态意味着如果后续处理需要前⾯的信息,则它必须重传,这样可能导致每次连接传送的数据量增⼤。另⼀⽅⾯,在服务器不需要先前信息时它的应答就较快。
注意:其中(4)(5)是⾯试中常⽤的⾯试题。虽然HTTP协议(应⽤层)是⽆连接,⽆状态的,但其所依赖的TCP协议(传输层)却是常连接、有状态的,⽽TCP协议(传输层)⼜依赖于IP协议(⽹络层)。
2.HTTP消息的结构
(1)Request 消息分为3部分,第⼀部分叫请求⾏,第⼆部分叫http header消息头, 第三部分是body正⽂,
header和body之间有个空⾏,结构如下图
(2)Response消息的结构,和Request消息的结构基本⼀样。同样也分为三部分,第⼀部分叫request line状态⾏,第⼆部分叫request header消息体,第三部分是body正⽂,header和body之间也有个空⾏,结构如下图
下⾯是使⽤Fiddler捕捉请求baidu的Request消息机构和Response消息机构:
因为没有输⼊任何表单信息,故request的消息正⽂为空,⼤家可以⼀个登录的页⾯试试看。
先到这⾥,HTTP协议的知识⽹上很丰富,在这⾥就不再熬述了。
⼆. ⽂件上传的三种实现
1. Jsp/servlet 实现⽂件上传
这是最常见也是最简单的⽅式
(1)实现⽂件上传的Jsp页⾯
(2)负责接⽂件的FileUploadServlet
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
// @WebServlet(name = "FileLoadServlet", urlPatterns = {"/fileload"})
public class FileLoadServlet extends HttpServlet {
private static Logger logger = Logger(FileLoadServlet.class);
private static final long serialVersionUID = 1302377908285976972L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { logger.info("------------ FileLoadServlet ------------");
if (ContentLength() > 0) {
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = InputStream();
// 给新⽂件拼上时间毫秒,防⽌重名
long now = System.currentTimeMillis();
File file = new File("c:/", "file-" + now + ".txt");
outputStream = new FileOutputStream(file);
byte temp[] = new byte[1024];
int size = -1;
while ((size = ad(temp)) != -1) { // 每次读取1KB,直⾄读完
outputStream.write(temp, 0, size);
}
logger.info("File load success.");
} catch (IOException e) {
logger.warn("File load fail.", e);
} finally {
outputStream.close();
inputStream.close();
}
}
}
}
FileUploadServlet的配置,推荐采⽤servlet3.0注解的⽅式更⽅便
<servlet>
<servlet-name>FileLoadServlet</servlet-name>
<servlet-class>com.juxinli.servlet.FileLoadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileLoadServlet</servlet-name>
<url-pattern>/fileload</url-pattern>
</servlet-mapping>
(3)运⾏效果
点击"submit"
页⾯转向⽂件上传成功的页⾯,再去C盘看看,发现多了⼀个⽂件:,这个就是刚上传的⽂件
我们打开看看,发现和原来的⽂本有些不⼀样
结合前⾯讲的HTTP协议的消息结构,不难发现这些⽂本就是去掉"请求头"后的"Request消息体"。所以,如果要得到与上传⽂件⼀致的⽂本,还需要⼀些字符串操作,这些就留给⼤家了。
另外,⼤家可以试试⼀个Jsp页⾯上传多个⽂件,会有不⼀样的精彩哦o(∩_∩)o ,不解释。
2. 模拟Post请求/servlet 实现⽂件上传
刚才我们是使⽤Jsp页⾯来上传⽂件,假如客户端不是webapp项⽬呢,显然刚才的那种⽅式有些捉襟见衬了。
这⾥我们换种思路,既然页⾯上通过点击可以实现⽂件上传,为何不能通过HttpClient来模拟浏览器发送上传⽂件的请求呢。关于HttpClient ,⼤家可以⾃⼰去了解。
(1)还是这个项⽬,启动servlet服务
(2)模拟请求的FileLoadClient
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apachemons.httpclient.HttpClient;
import org.apachemons.httpclient.HttpStatus;
import org.hods.PostMethod;
import org.hods.multipart.FilePart;
import org.hods.multipart.MultipartRequestEntity;
import org.hods.multipart.Part;
import org.apache.log4j.Logger;
public class FileLoadClient {
private static Logger logger = Logger(FileLoadClient.class);
public static String fileload(String url, File file) {
String body = "{}";
if (url == null || url.equals("")) {
return "参数不合法";
}
if (!ists()) {
return "要上传的⽂件名不存在";
}
PostMethod postMethod = new PostMethod(url);
try {
// FilePart:⽤来上传⽂件的类,file即要上传的⽂件
FilePart fp = new FilePart("file", file);
Part[] parts = { fp };
// 对于MIME类型的请求,httpclient建议全⽤MulitPartRequestEntity进⾏包装
MultipartRequestEntity mre = new MultipartRequestEntity(parts, Params());
postMethod.setRequestEntity(mre);
HttpClient client = new HttpClient();
// 由于要上传的⽂件可能⽐较⼤ , 因此在此设置最⼤的连接超时时间
int status = uteMethod(postMethod);
if (status == HttpStatus.SC_OK) {
InputStream inputStream = ResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while ((str = br.readLine()) != null) {
stringBuffer.append(str);
}
body = String();
} else {
body = "fail";
}
} catch (Exception e) {
logger.warn("上传⽂件异常", e);
} finally {
// 释放连接
}
return body;
}
public static void main(String[] args) throws Exception {
String body = fileload("localhost:8080/jsp_upload-servlet/fileload", new File("C:/"));
System.out.println(body);
}
}
(3)在Eclipse中运⾏FileLoadClient程序来发送请求,运⾏结果:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body><h2>File upload success</h2><a href="index.jsp">return</a></body> </html>
打印了:⽂件上传成功的succ.jsp页⾯
有没有发现什么,是不是和前⾯Jsp页⾯上传的结果类似?对的,还是去掉"请求头"后的"Request消息体"。
这种⽅式也很简单,负责接收⽂件的FileUploadServlet没有变,只要在客户端把⽂件读取到流中,然后模拟请求servlet就⾏了。
3.模拟Post请求/Controller(SpringMvc)实现⽂件上传
终于到第三种⽅式了,主要难点在于搭建maven+jetty+springmvc环境,接收⽂件的service和模拟请
求的客户端和上⾯相似。
(1)模拟请求的FileLoadClient未变
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apachemons.httpclient.HttpClient;
import org.apachemons.httpclient.HttpStatus;
import org.hods.PostMethod;
import org.hods.multipart.FilePart;
import org.hods.multipart.MultipartRequestEntity;
import org.hods.multipart.Part;
import org.apache.log4j.Logger;
public class FileLoadClient {
private static Logger logger = Logger(FileLoadClient.class);
public static String fileload(String url, File file) {
String body = "{}";
if (url == null || url.equals("")) {
return "参数不合法";
}
if (!ists()) {
return "要上传的⽂件名不存在";
}
PostMethod postMethod = new PostMethod(url);
html如何下载try {
// FilePart:⽤来上传⽂件的类,file即要上传的⽂件
FilePart fp = new FilePart("file", file);
Part[] parts = { fp };
// 对于MIME类型的请求,httpclient建议全⽤MulitPartRequestEntity进⾏包装
MultipartRequestEntity mre = new MultipartRequestEntity(parts, Params());
postMethod.setRequestEntity(mre);
HttpClient client = new HttpClient();
// 由于要上传的⽂件可能⽐较⼤ , 因此在此设置最⼤的连接超时时间
int status = uteMethod(postMethod);
if (status == HttpStatus.SC_OK) {
InputStream inputStream = ResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while ((str = br.readLine()) != null) {
stringBuffer.append(str);
}
body = String();
} else {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论