永久链接: http://gaojingsong.iteye.com/blog/2414484
预览文章: 【Http文件上传协议解析】
核心代码:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class SocketUploadUtil {
public static void main(String[] args) throws Exception {
// 判断端口号
int port = 9090;
// 构建socket,用于与服务器的联接
Socket socket = new Socket("127.0.0.1", port);
String path ="http://localhost:9090/mgr/upload";
String file = "c:/examples.cfg";
HashMap<String, String> params = new HashMap<String, String>();
params.put("email", "demo@123.com");
FileProperty f = new FileProperty("examples.cfg", new File(file), "file1", "text/plain");
List<FileProperty> files = new ArrayList<FileProperty>();
files.add(f);
uploadFile(socket,port, path, params,files);
}
/**
* descript: 用于文件上传的帮助类
* 直接通过http协议提交数据到服务器。 实现类似于下面web页面提交数据的功能
<form method="post"
action="upload"
enctype="multipart/form-data">
<label>email:</label>
<input type="text" name="email" value="kickscar@gmail.com">
<br><br>
<label>File1:</label>
<input type="file" name="file1">
<br><br>
<label>File2:</label>
<input type="file" name="file2">
<br><br>
<br>
<input type="submit" value="upload">
</form>
* @throws IOException
* @throws UnknownHostException
*/
public static boolean uploadFile(Socket socket,int port,
String path, Map<String, String> params,
List<FileProperty> files) throws UnknownHostException, IOException {
Random r=new Random();
// 属性数据的分隔线
final String BOUNDARY = "---------------------------"+ r.nextInt(9999999);
final String ENDLINE = "--" + BOUNDARY + "--\r\n";
long fileInEntityDataLength = 0; // 存http协议实体部分文件数据的总长度
// 下面开始计算http协议实体部分的总长度
// 1.先计算文件部分包括文件属性和文件内容的长度
for (FileProperty uploadFile : files) {
StringBuilder sb = new StringBuilder();
sb.append("--");
sb.append(BOUNDARY);
sb.append("\r\n");
sb.append("Content-Disposition: form-data; name=\""
+ uploadFile.getParameterName() + "\";filename=\""
+ uploadFile.getFileName() + "\"\r\n");
sb.append("Content-Type: " + uploadFile.getContenttype()
+ "\r\n\r\n");
sb.append("\r\n");
// 以上形成了文件的属性值部分的参数长度
fileInEntityDataLength += sb.toString().length();
// 再加上文件内容的总长度
if (uploadFile.getInputStream() != null) { // 如果用的是文件流的话,就计算流文件长度
fileInEntityDataLength += uploadFile.getFile().length();
} else { // 否则计算二进制文件长度
fileInEntityDataLength += uploadFile.getData().length;
}
}
// 2.再计算文本属性部分的长度
StringBuilder sb2 = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
sb2.append("--");
sb2.append(BOUNDARY);
sb2.append("\r\n");
sb2.append("Content-Disposition: form-data; name=\""
+ entry.getKey() + "\"\r\n\r\n");
sb2.append(entry.getValue());
sb2.append("\r\n");
}
// 计算传输给服务器的实体数据的总长度
// 实体数据总长度=文本数据总长+文件内容总长+结束语长度
long datalength = sb2.toString().getBytes().length + fileInEntityDataLength+ ENDLINE.getBytes().length;
// 构建URL对象,用于联接网络
URL url = new URL(path);
// 输出流
OutputStream outputStream = socket.getOutputStream();
// 下面完成http请求头的拼接和发送
StringBuffer protocal = new StringBuffer( "POST " + url.getPath() + " HTTP/1.1\r\n");
protocal.append( "Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, " +
"image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n");
protocal.append( "Accept-Language: zh-CN\r\n" );
//以下的两行最重要
protocal.append( "Content-Type: multipart/form-data; boundary="+ BOUNDARY + "\r\n" );
protocal.append( "Content-Length: " + datalength + "\r\n" );
protocal.append( "Connection: Keep-Alive\r\n" );
protocal.append( "Host: " + url.getHost() + ":" + port + "\r\n" );
protocal.append( "\r\n" );
System.out.println("打印一下http头信息:"+ protocal.toString());
//将请求的命令行和请求头信息发出
outputStream.write( protocal.toString().getBytes());
// 再将文件中的所有的文本类型的普通参数数据都发送出去
outputStream.write(sb2.toString().getBytes());
// 将所有文件类型的实体数据发送出去
for (FileProperty fp : files) {
StringBuilder fsb = new StringBuilder();
fsb.append("--");
fsb.append(BOUNDARY);
fsb.append("\r\n");
fsb.append(
"Content-Disposition: form-data; name=\""
+ fp.getParameterName() + "\"; filename=\""
+ fp.getFileName() + "\"").append("\r\n");
fsb.append("Content-Type: " + fp.getContenttype() + "\r\n\r\n");
outputStream.write(fsb.toString().getBytes());
// 发送文件真正的数据
if (fp.getInputStream() != null) { // 是大文件的话,用流输出
byte[] buffer = new byte[2048];
int length = 0;
while ((length = fp.getInputStream().read(buffer, 0, 2048)) != -1) {
outputStream.write(buffer, 0, length);
}
fp.getInputStream().close(); // 关闭输入流
} else { // 是小文件的话,用byte[] data输出
outputStream.write(fp.getData(), 0, fp.getData().length);
}
outputStream.write("\r\n".getBytes());
}
// 最后是发送数据的结束标志,表示数据发送完毕
outputStream.write(ENDLINE.getBytes());
outputStream.flush();
// 读取服务器端的回应
BufferedReader read = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
//System.out.println(read.readLine());
if (read.readLine().indexOf("200") == -1) {
return false;
}
outputStream.close();
read.close();
socket.close();
return true;
}
}
结果验证
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
/**
* 要上传的文件的属性的封装类
*/
public class FileProperty {
private byte[] data; //要上传的数据,小数据量上传
private InputStream inputStream; //要上传的数据的输入流,用于大文件上传真
private File file; //要上传的文件对象
private String fileName; //要上传的文件名
private String parameterName; //要上传的文件的请求参数名称
private String contenttype="application/octet-stream";
/**
* 此构造方法用于上传一些较小的数据.
* @param filename
* @param data : 只适合存储较少的数据
* @param paramterName
* @param contenttype
*/
public FileProperty(String filename,byte[] data,String paramterName,String contenttype){
this.data=data;
this.fileName=filename;
this.parameterName=paramterName;
if(contenttype!=null){
this.contenttype=contenttype;
}
}
/**
* 可以传较大的文件
* @param filename 文件名
* @param file 要上传的文件
* @param parameterName 文件的参数名字
* @param contenttype 文件类型
*/
public FileProperty(String filename,File file,String parameterName,String contenttype){
this.fileName=filename;
this.parameterName=parameterName;
this.file=file;
try {
this.inputStream=new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if(contenttype!=null){
this.contenttype=contenttype;
}
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getParameterName() {
return parameterName;
}
public void setParameterName(String parameterName) {
this.parameterName = parameterName;
}
public String getContenttype() {
return contenttype;
}
public void setContenttype(String contenttype) {
this.contenttype = contenttype;
}
}
相关推荐
此次学习的是HTTP的post协议,使用使用multipart form-data上传文件。 HTTP1.1封装 简单封装了HTTP1.1的get和post,实现http的长连接模式,实现发送和数据接收。简单实现了socket的复用。 详细介绍参考:...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机...
Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
实例115 点对面通信(Socket基于TCP/IP协议) 327 实例116 多线程断点续传(基于HTTP) 332 实例117 代理服务器的实现 340 实例118 IP多点传送(基于UDP的C/S) 345 第14章 线程 350 实例119 启动和停止线程 ...
01 selctors实现文件上传与下载 02 html的介绍 03 html文档树的概念 04 meta标签以及一些基本标签 05 img标签和列表标签 06 form表单之input标签 07 通过form向server端发送数据 08 form表单之select标签 09 table...
----------------------------...• 优化的图形库 包括定制的 2D 图形库, 3D 图形库基于 OpenGL ES 1.0 (硬件加速可选) • SQLite SQLite SQLite SQLite 用作结构化的数据存储 • 多媒体支持 包括常见的音频、视频和...