网上使用HttpURLConnection通过get或post请求传递参数或者传递文件的例子有很多。但是同时传递参数和文件,服务的并接收参数和文件的例子很少!此文将介绍同时发送参数和文件并接收。
1.HttpURLConnection简介
任何网络连接都需要经过socket才能连接,HttpURLConnection不需要设置socket,所以,HttpURLConnection并不是底层的连接,而是在底层连接上的一个请求。这就是为什么HttpURLConneciton只是一个抽象类,自身不能被实例化的原因。HttpURLConnection只能通过URL.openConnection()方法创建具体的实例。
虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。
简单来说,HttpURLConnection就是Java提供的发起HTTP请求的基础类库,提供了HTTP请求的基本能力
2.使用HttpURLConnection发送参数和文件
发送参数及文件
public String sendFileAndDataTest() throws Exception {
String serverUrl = "http://localhost/payment-wxrefund/wxrefund/refund";
Map<String, Object> reqData = new HashMap<>();
reqData.put("out_trade_no", “out_trade_no”);
reqData.put("transaction_id", “transaction_id”);
// 获取到要上传的文件的输入流信息,通过ByteArrayOutputStream流转成byte[]
File file = new File("D:\Download/2020-11-10包医微信账单.csv");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] body_data = null;
int c = 0;
byte[] buffer = new byte[8 * 1024];
try {
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
}
body_data = baos.toByteArray();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 调用自定义的post数据方法,提交表单数据及上传文件
String result = sendFileData(serverUrl, reqData, body_data, "utf-8");
}
private String sendFileData(String serverUrl, Map<String, Object> reqData, byte[] body_data, String charset) throws IOException {
// 设置三个常用字符串常量:换行、前缀、分界线(NEWLINE、PREFIX、BOUNDARY);
final String NEWLINE = "\r\n";
final String PREFIX = "--";
final String BOUNDARY = "#";
HttpURLConnection httpConn = null;
BufferedInputStream bis = null;
DataOutputStream dos = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// 实例化URL对象。调用URL有参构造方法,参数是一个url地址;
URL urlObj = new URL(serverUrl);
// 调用URL对象的openConnection()方法,创建HttpURLConnection对象;
httpConn = (HttpURLConnection) urlObj.openConnection();
// 调用HttpURLConnection对象setDoOutput(true)、setDoInput(true)、setRequestMethod("POST");
httpConn.setDoInput(true);
httpConn.setDoOutput(true);
httpConn.setRequestMethod("POST");
// 设置Http请求头信息;(Accept、Connection、Accept-Encoding、Cache-Control、Content-Type、User-Agent)
httpConn.setUseCaches(false);
httpConn.setRequestProperty("Connection", "Keep-Alive");
httpConn.setRequestProperty("Accept", "*/*");
httpConn.setRequestProperty("Accept-Encoding", "gzip, deflate");
httpConn.setRequestProperty("Cache-Control", "no-cache");
// 这个比较重要,按照上面分析的拼装出Content-Type头的内容
httpConn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY);
// 这个参数可以参考浏览器中抓出来的内容写,用chrome或者Fiddler抓吧看看就行
httpConn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)");
// 调用HttpURLConnection对象的connect()方法,建立与服务器的真实连接;
httpConn.connect();
// 调用HttpURLConnection对象的getOutputStream()方法构建输出流对象;
dos = new DataOutputStream(httpConn.getOutputStream());
// 获取表单中上传控件之外的控件数据,写入到输出流对象(根据上面分析的抓包的内容格式拼凑字符串);
if (reqData != null && !reqData.isEmpty()) { // 这时请求中的普通参数,键值对类型的,相当于上面分析的请求中的username,可能有多个
for (Map.Entry<String, Object> entry : reqData.entrySet()) {
String key = entry.getKey(); // 键,相当于上面分析的请求中的username
Object value = reqData.get(key); // 值,相当于上面分析的请求中的sdafdsa
dos.writeBytes(PREFIX + BOUNDARY + NEWLINE); // 像请求体中写分割线,就是前缀+分界线+换行
dos.writeBytes("Content-Disposition: form-data; "
+ "name=\"" + key + "\"" + NEWLINE); // 拼接参数名,格式就是Content-Disposition: form-data; name="key" 其中key就是当前循环的键值对的键,别忘了最后的换行
dos.writeBytes(NEWLINE); // 空行,一定不能少,键和值之间有一个固定的空行
dos.writeBytes(URLEncoder.encode(value.toString(), charset)); // 将值写入
// 或者写成:dos.write(value.toString().getBytes(charset));
dos.writeBytes(NEWLINE); // 换行
}
}
// 获取表单中上传附件的数据,写入到输出流对象(根据上面分析的抓包的内容格式拼凑字符串);
if (body_data != null && body_data.length > 0) {
dos.writeBytes(PREFIX + BOUNDARY + NEWLINE);// 像请求体中写分割线,就是前缀+分界线+换行
String fileName = "2020-11-10包医微信账单";
// 格式是:Content-Disposition: form-data; name="请求参数名"; filename="文件名"
// 我这里吧请求的参数名写成了uploadFile,是死的,实际应用要根据自己的情况修改
// 不要忘了换行
dos.writeBytes("Content-Disposition: form-data; " + "name=\""
+ "uploadFile" + "\"" + "; filename=\"" + fileName
+ "\"" + NEWLINE);
// 换行,重要!!不要忘了
dos.writeBytes(NEWLINE);
dos.write(body_data); // 上传文件的内容
dos.writeBytes(NEWLINE); // 最后换行
}
dos.writeBytes(PREFIX + BOUNDARY + PREFIX + NEWLINE);
// 最后的分割线,与前面的有点不一样是前缀+分界线+前缀+换行,最后多了一个前缀
dos.flush();
// 调用HttpURLConnection对象的getInputStream()方法构建输入流对象;
byte[] buffer = new byte[8 * 1024];
int c = 0;
// 调用HttpURLConnection对象的getResponseCode()获取客户端与服务器端的连接状态码。如果是200,则执行以下操作,否则返回null;
if (httpConn.getResponseCode() == 200) {
bis = new BufferedInputStream(httpConn.getInputStream());
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
}
}
// 将输入流转成字节数组,返回给客户端。
return new String(baos.toByteArray(), charset);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (dos != null)
dos.close();
if (bis != null)
bis.close();
if (baos != null)
baos.close();
httpConn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
接收参数及文件
public Map<String, String> getFileAndData(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取file
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;
MultiValueMap<String, MultipartFile> multiFileMap = multiRequest.getMultiFileMap();
List<MultipartFile> uploadFile1 = multiFileMap.get("uploadFile");
MultipartFile multipartFile = uploadFile1.get(0);
File file = File.createTempFile(multipartFile.getOriginalFilename(),".csv");
multipartFile.transferTo(file);
// 获取退款订单参数
String out_trade_no = request.getParameter("out_trade_no");
String transaction_id = request.getParameter("transaction_id");
}
亲测可用,欢迎留言,希望对大家有帮助。