在某些情况下,需要用Java applicatioin来模拟form,向服务器(本文以servlet为例)发送http post请求,包括提交表单域中的数据以及上传文件。如果仅仅是传递form中的数据,而不包含上传文件,那是很简单的,比如Java application可以这么写:

package com.pat.postrequestemulator;
importjava.io.BufferedReader;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.OutputStreamWriter;
importjava.net.HttpURLConnection;
importjava.net.URL;
public class PostRequestEmulator
{
public static void main(String[] args)throws Exception
{
// 服务地址
URL url = newURL("http://127.0.0.1:8080/test/upload");
// 设定连接的相关参数
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
OutputStreamWriter out = newOutputStreamWriter(connection.getOutputStream(), "UTF-8");
// 向服务端发送key = value对
out.write("username=kevin&password=pass");
out.flush();
out.close();
// 获取服务端的反馈
String strLine="";
String strResponse ="";
InputStream in =connection.getInputStream();
BufferedReader reader = newBufferedReader(new InputStreamReader(in));
while((strLine =reader.readLine()) != null)
{
strResponse +=strLine +"\n";
}
System.out.print(strResponse);
}
}

服务端的servlet可以这么写:

packagecom.pat.handlinghttprequestservlet;
importjava.io.IOException;
importjava.io.PrintWriter;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
public class HandlingHttpRequestServlet extends HttpServlet
{
private static final longserialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequestreq, HttpServletResponse resp)
throws ServletException, IOException
{
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throwsServletException, IOException
{
String username =req.getParameter("username");          //获取username所对应的value
String password =req.getParameter("password");          //获取password所对应的value
System.out.println("Thereceived username and password is: " + username + "/" +password);
// 向请求端发回反馈信息
PrintWriter out =resp.getWriter();
out.print("OK");
out.flush();
out.close();
super.doPost(req, resp);
}
}

一切看起来都不复杂。但是如果要模拟的表单,除了要向服务器传递如上面的“key = value”这样的普通信息,同时还要上传文件,事情就复杂得多。下面详解如下:

1. 准备

玄机逸士很久没有开发web方面的应用了,所以机器上没有现成的环境,为此先要进行这方面的准备。

如:D:\Tomcat6

和commons-io-2.1-bin.zip, commons-fileupload依赖于commons-io,但在编程的过程中,

不会直接用到commons-io

c)  检查Tomcat的安装是否成功。双击D:\Tomcat6\bin目录中的startup.bat文件,就可以启动tomcat。

打开浏览器,访问http://localhost:8080/,如果出现tomcat相关的页面,则说明tomcat安装成功。

d)  在D:\Tomcat6\webapps目录下创建一个test子目录,我们等会开发的servlet就将部署在这里。在

test目录下再建立两个目录WEB-INF(必须大写)和upload,在WEB-INF下面 创建两个目录classes和lib,

同时新建一个web.xml文件;在upload目录下,创建一个temp子目录,这些工作做完以后,test目录

下面的目录文件结构如下图所示。

其中的classes目录,用来存放将要开发的servlet,lib用来存放commons-fileupload和commons-io相关的jar包,web.xml是一些应用描述信息;upload用于存放客户端(即我们要开发的Java

application)上传过来的文件,temp则用于存放上传过程中可能产生的一些临时文件。

e)  将commons-fileupload-1.2.2-bin.zip和commons-io-2.1-bin.zip解压,可以得到commons-fileupload-

1.2.2.jar和commons-io-2.1.jar,我们将这两个文件拷贝到d)中创建的lib目录中。

f)   编辑web.xml使之如下:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
Hello
com.pat.handlinghttprequestservlet.HandlingHttpRequestServlet
Hello
/upload

这个web.xml可以先从D:\Tomcat6\webapps\examples\WEB-INF下面拷贝过来,再进行如上黑体字所示的修改即可。标签描述的是servlet代码方面的资料,其中Hello是给这个servlet起一个名字Hello,它必须和下面的中的一致,该名字具体是什么可以随意写定。

com.pat.handlinghttprequestservlet.HandlingHttpRequestServlet说明了完整的servlet类名,即servlet的类名为HandlingHttpRequestServlet,它位于包com.pat.handlinghttprequestservlet中。

/upload,说明了如果客户端向“http://Web服务器的URL地址:端口号/test/upload”发出了请求,则该请求将由位于包com.pat.handlinghttprequestservlet中的HandlingHttpRequestServlet进行处理。

到此,前期准备工作结束。下面准备写代码。

2.       Servlet的代码

packagecom.pat.handlinghttprequestservlet;
importjava.io.File;
importjava.io.IOException;
importjava.io.PrintWriter;
importjava.util.ArrayList;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.apache.commons.fileupload.FileItem;
importorg.apache.commons.fileupload.disk.DiskFileItemFactory;
importorg.apache.commons.fileupload.servlet.ServletFileUpload;
public class HandlingHttpRequestServlet extends HttpServlet
{
private static final longserialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequestreq, HttpServletResponse resp)
throws ServletException, IOException
{
super.doGet(req, resp);
}
@SuppressWarnings({"unchecked", "deprecation" })
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throwsServletException, IOException
{
DiskFileItemFactory factory =new DiskFileItemFactory();
//得到绝对文件夹路径,比如"D:\\Tomcat6\\webapps\\test\\upload"
String path = req.getRealPath("/upload");
//临时文件夹路径
String repositoryPath =req.getRealPath("/upload/temp");
// 设定临时文件夹为repositoryPath
factory.setRepository(newFile(repositoryPath));
// 设定上传文件的阈值,如果上传文件大于1M,就可能在repository
// 所代 表的文件夹中产生临时文件,否则直接在内存中进行处理
factory.setSizeThreshold(1024* 1024);
//System.out.println("----"+ req.getContextPath());  // 得到相对文件夹路径,比如 "/test"
// 创建一个ServletFileUpload对象
ServletFileUpload uploader =new ServletFileUpload(factory);
try
{
// 调用uploader中的parseRequest方法,可以获得请求中的相关内容,
// 即一个FileItem类型的ArrayList。FileItem是在
// org.apache.commons.fileupload中定义的,它可以代表一个文件,
// 也可以代表一个普通的form field
ArrayListlist = (ArrayList)uploader.parseRequest(req);
System.out.println(list.size());
for(FileItemfileItem : list)
{
if(fileItem.isFormField())      // 如果是普通的form field
{
Stringname = fileItem.getFieldName();
Stringvalue = fileItem.getString();
System.out.println(name+ " = " + value);
}
else   // 如果是文件
{
Stringvalue = fileItem.getName();
intstart = value.lastIndexOf("\\");
StringfileName = value.substring(start + 1);
// 将其中包含的内容写到path(即upload目录)下,
// 名为fileName的文件中
fileItem.write(newFile(path, fileName));
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
// 向客户端反馈结果
PrintWriter out =resp.getWriter();
out.print("OK");
out.flush();
out.close();
super.doPost(req, resp);
}
}

再在classes目录建立如下目录结构com\pat\handlinghttprequestservlet,这用来代表HandlingHttpRequestServlet这个servlet所在的包名,将编译好的HandlingHttpRequestServlet.class,拷贝到这个目录下,然后启动(或者重新启动)tomcat

3.       Java application的代码

package com.pat.postrequestemulator;
importjava.io.BufferedReader;
importjava.io.DataInputStream;
importjava.io.File;
import java.io.FileInputStream;
importjava.io.InputStream;
importjava.io.InputStreamReader;
importjava.io.OutputStream;
importjava.io.Serializable;
importjava.net.HttpURLConnection;
importjava.net.URL;
importjava.util.ArrayList;
public classPostRequestEmulator
{
public static void main(String[] args)throws Exception
{
// 设定服务地址
String serverUrl ="http://127.0.0.1:8080/test/upload";
// 设定要上传的普通Form Field及其对应的value
// 类FormFieldKeyValuePair的定义见后面的代码
ArrayList ffkvp = new ArrayList();
ffkvp.add(newFormFieldKeyValuePair("username", "Patrick"));
ffkvp.add(newFormFieldKeyValuePair("password", "HELLOPATRICK"));
ffkvp.add(newFormFieldKeyValuePair("hobby", "Computer programming"));
// 设定要上传的文件。UploadFileItem见后面的代码
ArrayList ufi = new ArrayList();
ufi.add(newUploadFileItem("upload1", "E:\\Asturias.mp3"));
ufi.add(newUploadFileItem("upload2", "E:\\full.jpg"));
ufi.add(newUploadFileItem("upload3", "E:\\dyz.txt"));
// 类HttpPostEmulator的定义,见后面的代码
HttpPostEmulator hpe = new HttpPostEmulator();
String response =hpe.sendHttpPostRequest(serverUrl, ffkvp, ufi);
System.out.println("Responsefrom server is: " + response);
}
}
classHttpPostEmulator
{
//每个post参数之间的分隔。随意设定,只要不会和其他的字符串重复即可。
private static final String BOUNDARY ="----------HV2ymHFg03ehbqgZCaKO6jyH";
public StringsendHttpPostRequest(String serverUrl,
ArrayListgeneralFormFields,
ArrayListfilesToBeUploaded) throws Exception
{
// 向服务器发送post请求
URL url = newURL(serverUrl/*"http://127.0.0.1:8080/test/upload"*/);
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
// 发送POST请求必须设置如下两行
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection","Keep-Alive");
connection.setRequestProperty("Charset","UTF-8");
connection.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);
// 头
String boundary = BOUNDARY;
// 传输内容
StringBuffer contentBody =new StringBuffer("--" + BOUNDARY);
// 尾
String endBoundary ="\r\n--" + boundary + "--\r\n";
OutputStream out =connection.getOutputStream();
// 1. 处理普通表单域(即形如key = value对)的POST请求
for(FormFieldKeyValuePairffkvp : generalFormFields)
{
contentBody.append("\r\n")
.append("Content-Disposition: form-data; name=\"")
.append(ffkvp.getKey() + "\"")
.append("\r\n")
.append("\r\n")
.append(ffkvp.getValue())
.append("\r\n")
.append("--")
.append(boundary);
}
String boundaryMessage1 =contentBody.toString();
out.write(boundaryMessage1.getBytes("utf-8"));
// 2. 处理文件上传
for(UploadFileItem ufi :filesToBeUploaded)
{
contentBody = newStringBuffer();
contentBody.append("\r\n")
.append("Content-Disposition:form-data; name=\"")
.append(ufi.getFormFieldName() +"\"; ")   // form中field的名称
.append("filename=\"")
.append(ufi.getFileName() +"\"")   //上传文件的文件名,包括目录
.append("\r\n")
.append("Content-Type:application/octet-stream")
.append("\r\n\r\n");
StringboundaryMessage2 = contentBody.toString();
out.write(boundaryMessage2.getBytes("utf-8"));
// 开始真正向服务器写文件
File file = newFile(ufi.getFileName());
DataInputStream dis= new DataInputStream(new FileInputStream(file));
int bytes = 0;
byte[] bufferOut =new byte[(int) file.length()];
bytes =dis.read(bufferOut);
out.write(bufferOut,0, bytes);
dis.close();
contentBody.append("------------HV2ymHFg03ehbqgZCaKO6jyH");
StringboundaryMessage = contentBody.toString();
out.write(boundaryMessage.getBytes("utf-8"));
//System.out.println(boundaryMessage);
}
out.write("------------HV2ymHFg03ehbqgZCaKO6jyH--\r\n".getBytes("UTF-8"));
// 3. 写结尾
out.write(endBoundary.getBytes("utf-8"));
out.flush();
out.close();
// 4. 从服务器获得回答的内容
String strLine="";
String strResponse ="";
InputStream in =connection.getInputStream();
BufferedReader reader = newBufferedReader(new InputStreamReader(in));
while((strLine =reader.readLine()) != null)
{
strResponse +=strLine +"\n";
}
//System.out.print(strResponse);
return strResponse;
}
}
// 一个POJO。用于处理普通表单域形如key = value对的数据
classFormFieldKeyValuePair implements Serializable
{
private static final longserialVersionUID = 1L;
// The form field used for receivinguser's input,
// such as "username" in ""
private String key;
// The value entered by user in thecorresponding form field,
// such as "Patrick" the abovementioned formfield "username"
private String value;
public FormFieldKeyValuePair(Stringkey, String value)
{
this.key = key;
this.value = value;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
}
// 一个POJO。用于保存上传文件的相关信息
classUploadFileItem implements Serializable
{
private static final longserialVersionUID = 1L;
// The form field name in a form used foruploading a file,
// such as "upload1" in ""
private String formFieldName;
// File name to be uploaded, thefileName contains path,
// such as "E:\\some_file.jpg"
private String fileName;
public UploadFileItem(StringformFieldName, String fileName)
{
this.formFieldName =formFieldName;
this.fileName = fileName;
}
public String getFormFieldName()
{
return formFieldName;
}
public void setFormFieldName(StringformFieldName)
{
this.formFieldName =formFieldName;
}
public String getFileName()
{
return fileName;
}
public void setFileName(StringfileName)
{
this.fileName = fileName;
}
}

4.       运行结果

运行PostRequestEmulator之前,服务器的upload目录下的情况:

运行PostRequestEmulator后,服务器的upload目录下的情况:

PostRequestEmulator从服务端得到的反馈情况:

Response fromserver is: OK

Tomcat控制台输出:

如果上传的文件中有大于1M的情况,第二次执行PostRequestEmulator的时候,就会在temp目录中产生临时文件。