转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23781773

文件上传是我们项目中经常使用的功能,一般我们的服务器可能都是web服务器,当我们使用非浏览器客户端上传文件时,比如手机(Android)等上传,可能就需要对传输的数据进行规范化的拼接,说白了,就是我们得自己完成浏览器帮我们做的事。

我首先写了服务器端代码,用来接收我们的数据,一会会贴出源码。然后写了个web页面用于上次,便于我们看其中的原理。

当点击了上传以后,这里我使用了firefox的firebug来观察网络信息,可以看到发出了一个POST请求,下面我框出的是请求头信息。里面包含一些请求的配置数据。

接下来看这张图:

我们可以看到我们发送的数据,一个是name为username的普通表单数据,一个为name为uploadFile的一个文件数据,可以看得出来,浏览器把文件数据转化成了2进制然后按特定的格式发给服务器了。

好了,下面开始实现上传,模拟浏览器的操作。

1、使用HttpUrlConnection

private static final String BOUNDARY = "----WebKitFormBoundaryT1HoybnYeFOGFlBR";

	/**
*
* @param params
* 传递的普通参数
* @param uploadFile
* 需要上传的文件名
* @param fileFormName
* 需要上传文件表单中的名字
* @param newFileName
* 上传的文件名称,不填写将为uploadFile的名称
* @param urlStr
* 上传的服务器的路径
* @throws IOException
*/
public void uploadForm(Map<String, String> params, String fileFormName,
File uploadFile, String newFileName, String urlStr)
throws IOException {
if (newFileName == null || newFileName.trim().equals("")) {
newFileName = uploadFile.getName();
} StringBuilder sb = new StringBuilder();
/**
* 普通的表单数据
*/
for (String key : params.keySet()) {
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + key + "\""
+ "\r\n");
sb.append("\r\n");
sb.append(params.get(key) + "\r\n");
}
/**
* 上传文件的头
*/
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + fileFormName
+ "\"; filename=\"" + newFileName + "\"" + "\r\n");
sb.append("Content-Type: image/jpeg" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType
sb.append("\r\n"); byte[] headerInfo = sb.toString().getBytes("UTF-8");
byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
System.out.println(sb.toString());
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY);
conn.setRequestProperty("Content-Length", String
.valueOf(headerInfo.length + uploadFile.length()
+ endInfo.length));
conn.setDoOutput(true); OutputStream out = conn.getOutputStream();
InputStream in = new FileInputStream(uploadFile);
out.write(headerInfo); byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1)
out.write(buf, 0, len); out.write(endInfo);
in.close();
out.close();
if (conn.getResponseCode() == 200) {
System.out.println("上传成功");
} }

我详细解释一下,首先我拼接了需要发送的数据,其实就是咱们在图三中看到的数据,然后使用HttpUrlConnetion设置了一系列属性其实就是在设置图二中看到的请求头信息。

于是,我们完成了请求头的设置,以及需要上传数据的拼接,所以我们完成了浏览器的工作,自然就实现文件上传了。

2、使用Socket实现文件上传,参数基本一致,使用HttpUrlConnection上传有一个很致命的问题就是,当上传文件很大时,会发生内存溢出,手机分配给我们app的内存更小,所以就更需要解决这个问题,于是我们可以使用Socket模拟POST进行HTTP文件上传。

	/**
*
* @param params
* 传递的普通参数
* @param uploadFile
* 需要上传的文件名
* @param fileFormName
* 需要上传文件表单中的名字
* @param newFileName
* 上传的文件名称,不填写将为uploadFile的名称
* @param urlStr
* 上传的服务器的路径
* @throws IOException
*/
public void uploadFromBySocket(Map<String, String> params,
String fileFormName, File uploadFile, String newFileName,
String urlStr) throws IOException {
if (newFileName == null || newFileName.trim().equals("")) {
newFileName = uploadFile.getName();
} StringBuilder sb = new StringBuilder();
/**
* 普通的表单数据
*/ if (params != null)
for (String key : params.keySet()) {
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + key
+ "\"" + "\r\n");
sb.append("\r\n");
sb.append(params.get(key) + "\r\n");
} else{ab.append("\r\n");}
/**
* 上传文件的头
*/
sb.append("--" + BOUNDARY + "\r\n");
sb.append("Content-Disposition: form-data; name=\"" + fileFormName
+ "\"; filename=\"" + newFileName + "\"" + "\r\n");
sb.append("Content-Type: image/jpeg" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType
sb.append("\r\n"); byte[] headerInfo = sb.toString().getBytes("UTF-8");
byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8"); System.out.println(sb.toString()); URL url = new URL(urlStr);
Socket socket = new Socket(url.getHost(), url.getPort());
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os, true, "UTF-8"); // 写出请求头
ps.println("POST " + urlStr + " HTTP/1.1");
ps.println("Content-Type: multipart/form-data; boundary=" + BOUNDARY);
ps.println("Content-Length: "
+ String.valueOf(headerInfo.length + uploadFile.length()
+ endInfo.length));
ps.println("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); InputStream in = new FileInputStream(uploadFile);
// 写出数据
os.write(headerInfo); byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1)
os.write(buf, 0, len); os.write(endInfo); in.close();
os.close();
}

这里因为我们使用的是Socket,所以自然对于请求头,我们也需要自己拼接了,没有什么属性设置了。参考图二框出的部分,我们使用PrintStream完成了请求头的拼接,接下来就是数据的拼接,这和使用HttpUrlConnection的方式一致。我们也完成了数据的上传。

最后测试我们的代码:

public static void main(String[] args) {
try { File file = new File("D:/dtd", "dwr30.dtd"); new Test().uploadForm(null, "uploadFile", file, "helloworld.txt",
"http://localhost:8080/strurts2fileupload/uploadAction"); new Test().uploadFromBySocket(null, "uploadFile", file,
"hibernate-configuration-3.0.dtd",
"http://localhost:8080/strurts2fileupload/uploadAction"); } catch (Exception e) {
e.printStackTrace();
}
}

效果:

如果这篇文章对你有帮助,赞一下~

源码点击此处下载

从原理角度解析Android (Java) http 文件上传的更多相关文章

  1. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  2. Java Web文件上传

    参考资料:http://www.cnblogs.com/xdp-gacl/p/4200090.html 一.问题描述 Java Web文件上传需要借助一些第三方库,常用的是借助Apache的包,有两个 ...

  3. H5+JAVA的文件上传,断点续传

    这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...

  4. java进行文件上传,带进度条

    网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...

  5. CentOS下安装配置NFS并通过Java进行文件上传下载

    1:安装NFS (1)安装 yum install nfs-utils rpcbind (2)启动rpcbind服务 systemctl restart rpcbind.service 查看服务状态 ...

  6. java实现文件上传下载

    喜欢的朋友可以关注下,粉丝也缺. 今天发现已经有很久没有给大家分享一篇技术文章了,于是想了一下给大家分享一篇java实现文件上传下载功能的文章,不喜欢的希望大家勿喷. 想必大家都知道文件的上传前端页面 ...

  7. Java实现文件上传到服务器(FTP方式)

    Java实现文件上传到服务器(FTP方式) 1,jar包:commons-net-3.3.jar 2,实现代码: //FTP传输到数据库服务器 private boolean uploadServer ...

  8. Java超大文件上传解决办法

    这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...

  9. java+大文件上传解决方案

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...

随机推荐

  1. 如何高效地向Redis插入大量的数据(转)

    最近有个哥们在群里问,有一个日志,里面存的是IP地址(一行一个),如何将这些IP快速导入到Redis中. 我刚开始的建议是Shell+redis客户端. 今天,查看Redis官档,发现文档的首页部分( ...

  2. 常见问题(FAQ) | VPNCUP

    常见问题(FAQ) | VPNCUP 常见问题(FAQ) 关于FAQ 新手开始 登录验证问题 为什么刚注册后,登录VPN服务器提示错误? 免费注册的用户有哪些限制? 为什么连接免费VPN后20分钟自动 ...

  3. VSTO之旅系列(一):VSTO入门

    原文:VSTO之旅系列(一):VSTO入门 引言: 因为工作的原因,这段时间一直在看VSTO的相关的内容的,因此希望通过这个系列来记录下我学习的过程和大家分享Office开发的相关知识,希望以后有朋友 ...

  4. java中线程中的相关知识点

    (1)守护线程必须在线程start前设置(2)守护线程在所有用户线程结束后,也会终止(3)由于(2)所有守护线程不能执行一些读写操作,原因:如果守护线程在执行读写操作时,如果用户线程结束了,守护线程的 ...

  5. libevent安装总结 - jinfg2008的专栏 - 博客频道 - CSDN.NET

    libevent安装总结 - jinfg2008的专栏 - 博客频道 - CSDN.NET libevent安装总结 分类: linux 系统配置 2013-02-13 22:37 99人阅读 评论( ...

  6. linux常用系统配置命令汇总

    系统配置及查看信息相关命令 # uname -a # 查看内核/操作系统/CPU信息# head -n 1 /etc/issue # 查看操作系统版本# cat /proc/cpuinfo # 查看C ...

  7. HDU 4815 背包

    标题的含义给出N问题.和概率P,然后给予相应的分数为每个问题x(每个问题只有两种选择,纠正错误). 两个人来回答.一个人是随机选择的答案,问:还有一个人的至少一些点的能力有保证P概率不会失败. 01背 ...

  8. 服务器编程入门(2)IP协议详解

    问题聚焦:     IP协议是TCP/IP协议族的核心协议,也是socket网络编程的基础之一.这里从两个方面较为深入地探讨IP协议:     1,IP头部信息(指定IP通信的源端IP地址,目的端IP ...

  9. 深入理解java虚拟机系列(一):java内存区域与内存溢出异常

    文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...

  10. 从零開始学习OpenCL开发(一)架构

    多谢大家关注 转载本文请注明:http://blog.csdn.net/leonwei/article/details/8880012 本文将作为我<从零開始做OpenCL开发>系列文章的 ...