本文记录如何使用rfc1867协议来上传文本文件(大小不能超过2M)
首先客户端上传文件时,服务器要收到客户端的请求就必须在客户端人工构造rfc1867协议规定的请求内容。
具体如下:
--UUID
Content-Disposition:form-data;name="字段名"
Content-Type:text/plain;charset=UTF-8 //如果上传的不是文本文件而是图片,则text/plain改为image/jpeg
Content-Transfer-Enconding:8dit //所传送的内容不符合默认的编码方式,须加上该标题头
字段属性值
--UUID
Content-Disposition:form-data;name="字段名";filename="文件名"
Content-Type:application/octet-stream;charset=UTF-8
文件内容(二进制流)
--UUID-- //最后要加上回车
其中UUID是随机生成的一串数字,作为分隔符。
下面是客户端的实现:
public class FileUploadActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode
.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites()
.detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects().penaltyLog()
.penaltyDeath().build());
Button button = new Button(this);
button.setText("上传");
setContentView(button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String actionUrl = "http://36078d58.nat123.cc/FileUploadAndDownload/UploadHandleServlet";
final Map<String, String> params = new HashMap<>();
params.put("strParamName", "strParamValue");
final Map<String, File> files = new HashMap<>();
files.put("android上传.txt", new File("/storage/emulated/0/eguan.txt")); // key为上传后的文件名,value为要上传的文件路径
new Thread(new Runnable() {
@Override
public void run() {
try {
String str = post(actionUrl, params, files);
Log.d("FileUploadActivity", "结果:" + str);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
}
public static String post(String actionUrl,
Map<String, String> params, Map<String, File> files)
throws IOException {
String BOUNDARY = java.util.UUID.randomUUID().toString();
String PREFIX = "--", LINEND = "\r\n";
String MULTIPART_FROM_DATA = "multipart/form-data";
String CHARSET = "UTF-8";
URL uri = new URL(actionUrl);
HttpURLConnection conn = (HttpURLConnection) uri
.openConnection();
conn.setReadTimeout(5 * 1000); // 缓存的最长时间
conn.setDoInput(true);// 允许输入
conn.setDoOutput(true);// 允许输出
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST");
conn.setRequestProperty("connection", "keep-alive"); //保持连接活跃
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA
+ ";boundary=" + BOUNDARY);
// 首先组拼文本类型的参数
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINEND);
sb.append("Content-Disposition: form-data; name=\""
+ entry.getKey() + "\"" + LINEND);
sb.append("Content-Type: text/plain; charset="
+ CHARSET + LINEND);
sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
sb.append(LINEND);
sb.append(entry.getValue());
sb.append(LINEND);
}
DataOutputStream outStream = new DataOutputStream(
conn.getOutputStream());
outStream.write(sb.toString().getBytes());
// 发送文件数据
if (files != null)
for (Map.Entry<String, File> file : files.entrySet()) {
StringBuilder sb1 = new StringBuilder();
sb1.append(PREFIX);
sb1.append(BOUNDARY);
sb1.append(LINEND);
sb1.append("Content-Disposition: form-data; name=\"file\"; filename=\""
+ file.getKey() + "\"" + LINEND);
sb1.append("Content-Type: application/octet-stream; charset="
+ CHARSET + LINEND);
sb1.append(LINEND);
outStream.write(sb1.toString().getBytes());
InputStream is = new FileInputStream(file.getValue());
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
is.close();
outStream.write(LINEND.getBytes());
}
// 请求结束标志
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND)
.getBytes();
outStream.write(end_data);
outStream.flush();
// 得到响应码
int res = conn.getResponseCode();
StringBuilder sb2 = null;
InputStream in = conn.getInputStream();
if (res == 200) {
int ch;
sb2 = new StringBuilder();
while ((ch = in.read()) != -1) {
sb2.append((char) ch);
}
}
outStream.close();
conn.disconnect();
if (sb2==null)
return null;
else
return sb2.toString();
}
}
注:
①真机测试
服务端与客户端不在同一个局域网,访问需要外网ip地址映射,如本文为:http://36078d58.nat123.cc/FileUploadAndDownload/UploadHandleServlet;客户端和服务端处在同一个局域网,则可将36078d58.nat123.cc改为服务端所在计算机的局域网ip地址,如192.168.x.x:8080。
②模拟器测试
客户端是android自带的模拟器的,ip改为10.0.2.2:8080;是第三方模拟器的,ip改为服务端所在计算机的ip地址。
下面是服务端的实现:
在eclipse下新建一个web项目,名为FileUploadAndDownload,在src目录下新建一个包,在包内新建一个java文件,名为UploadHandleServlet.java,然后将项目打包成war文件放到Tomcat服务器所在目录下的webapps文件夹内,并启动Tomcat。
@WebServlet("/UploadHandleServlet")
public class UploadHandleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public UploadHandleServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
// 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
File file = new File(savePath);
// 判断上传文件的保存目录是否存在
if (!file.exists() && !file.isDirectory()) {
System.out.println(savePath + "目录不存在,需要创建");
// 创建目录
file.mkdir();
}
// 消息提示
String message = "";
try {
// 使用Apache文件上传组件处理文件上传步骤:
// 1、创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2、创建一个文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
// 解决上传文件名的中文乱码
upload.setHeaderEncoding("UTF-8");
// 3、判断提交上来的数据是否是上传表单的数据
if (!ServletFileUpload.isMultipartContent(request)) {
// 按照传统方式获取数据
return;
}
// 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
// 如果fileitem中封装的是普通输入项的数据
if (item.isFormField()) {
String name = item.getFieldName();
// 解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
// value = new String(value.getBytes("GB2312"),"UTF-8");
System.out.println(name + "=" + value);
} else {// 如果fileitem中封装的是上传文件
// 得到上传的文件名称,
String filename = item.getName();
System.out.println(filename);
if (filename == null || filename.trim().equals("")) {
continue;
}
// 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:
// c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
// 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf("\\") + 1);
// 获取item中的上传文件的输入流
InputStream in = item.getInputStream();
// 创建一个文件输出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
// 创建一个缓冲区
byte buffer[] = new byte[1024];
// 判断输入流中的数据是否已经读完的标识
int len = 0;
// 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while ((len = in.read(buffer)) > 0) {
// 使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\"
// + filename)当中
out.write(buffer, 0, len);
}
// 关闭输入流
in.close();
// 关闭输出流
out.close();
// 删除处理文件上传时生成的临时文件
item.delete();
message = "文件上传成功!";
}
}
} catch (Exception e) {
message = "文件上传失败!";
e.printStackTrace();
}
request.setAttribute("message", message);
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
RequestContext req = new ServletRequestContext(request);
if (FileUpload.isMultipartContent(req)) {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
fileUpload.setHeaderEncoding("UTF-8");
fileUpload.setFileSizeMax(1024 * 1024 * 1024);
List items = new ArrayList();
try {
items = fileUpload.parseRequest(request);
} catch (Exception e) {
}
Iterator it = items.iterator();
while (it.hasNext()) {
FileItem fileItem = (FileItem) it.next();
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + " " + fileItem.getName() + " "
+ new String(fileItem.getString().getBytes("UTF-8"), "UTF-8"));
} else {
System.out.println(fileItem.getFieldName() + " " + fileItem.getName() + " " + fileItem.isInMemory()
+ " " + fileItem.getContentType() + " " + fileItem.getSize());
if (fileItem.getName() != null && fileItem.getSize() != 0) {
File fullFile = new File(fileItem.getName());
File newFile = new File("D:\\" + fullFile.getName());
try {
fileItem.write(newFile);
} catch (Exception E) {
}
response.getWriter().write("Success!");
} else {
System.out.println("no file choosen or empty file");
response.getWriter().print("Failure!");
}
}
}
}
}
}
注:客户端发送文件上传请求后会调用doPost方法,上传后的文件会保存在D盘,上传成功后会返回结果给客户端。doPost部分代码的解释可参照doGet