`
酷的飞上天空
  • 浏览: 517566 次
  • 性别: Icon_minigender_1
  • 来自: 无锡
社区版块
存档分类
最新评论

使用HttpUrlConnection进行post请求上传文件

阅读更多

使用HttpUrlConnection模拟post表单进行文件上传平时很少使用,比较麻烦。

 

原理是: 分析文件上传的数据格式,然后根据格式构造相应的发送给服务器的字符串。

格式如下:这里的httppost123是我自己构造的字符串,可以是其他任何的字符串

----------httppost123 (\r\n)
Content-Disposition: form-data; name="img"; filename="t.txt" (\r\n)
Content-Type: application/octet-stream (\r\n)

(\r\n)

sdfsdfsdfsdfsdf (\r\n)
----------httppost123 (\r\n)
Content-Disposition: form-data; name="text" (\r\n)

(\r\n)

text tttt (\r\n)
----------httppost123-- (\r\n)
(\r\n)

 

上面的(\r\n)表示各个数据必须以(\r\n)结尾。

 

具体Java代码如下:

 

 

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

public class HttpPostUtil {
	URL url;
	HttpURLConnection conn;
	String boundary = "--------httppost123";
	Map<String, String> textParams = new HashMap<String, String>();
	Map<String, File> fileparams = new HashMap<String, File>();
	DataOutputStream ds;

	public HttpPostUtil(String url) throws Exception {
		this.url = new URL(url);
	}
    //重新设置要请求的服务器地址,即上传文件的地址。
	public void setUrl(String url) throws Exception {
		this.url = new URL(url);
	}
    //增加一个普通字符串数据到form表单数据中
	public void addTextParameter(String name, String value) {
		textParams.put(name, value);
	}
    //增加一个文件到form表单数据中
	public void addFileParameter(String name, File value) {
		fileparams.put(name, value);
	}
    // 清空所有已添加的form表单数据
	public void clearAllParameters() {
		textParams.clear();
		fileparams.clear();
	}
    // 发送数据到服务器,返回一个字节包含服务器的返回结果的数组
	public byte[] send() throws Exception {
		initConnection();
		try {
			conn.connect();
		} catch (SocketTimeoutException e) {
			// something
			throw new RuntimeException();
		}
		ds = new DataOutputStream(conn.getOutputStream());
		writeFileParams();
		writeStringParams();
		paramsEnd();
		InputStream in = conn.getInputStream();
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int b;
		while ((b = in.read()) != -1) {
			out.write(b);
		}
		conn.disconnect();
		return out.toByteArray();
	}
    //文件上传的connection的一些必须设置
	private void initConnection() throws Exception {
		conn = (HttpURLConnection) this.url.openConnection();
		conn.setDoOutput(true);
		conn.setUseCaches(false);
		conn.setConnectTimeout(10000); //连接超时为10秒
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type",
				"multipart/form-data; boundary=" + boundary);
	}
    //普通字符串数据
	private void writeStringParams() throws Exception {
		Set<String> keySet = textParams.keySet();
		for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
			String name = it.next();
			String value = textParams.get(name);
			ds.writeBytes("--" + boundary + "\r\n");
			ds.writeBytes("Content-Disposition: form-data; name=\"" + name
					+ "\"\r\n");
			ds.writeBytes("\r\n");
			ds.writeBytes(encode(value) + "\r\n");
		}
	}
    //文件数据
	private void writeFileParams() throws Exception {
		Set<String> keySet = fileparams.keySet();
		for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
			String name = it.next();
			File value = fileparams.get(name);
			ds.writeBytes("--" + boundary + "\r\n");
			ds.writeBytes("Content-Disposition: form-data; name=\"" + name
					+ "\"; filename=\"" + encode(value.getName()) + "\"\r\n");
			ds.writeBytes("Content-Type: " + getContentType(value) + "\r\n");
			ds.writeBytes("\r\n");
			ds.write(getBytes(value));
			ds.writeBytes("\r\n");
		}
	}
    //获取文件的上传类型,图片格式为image/png,image/jpg等。非图片为application/octet-stream
	private String getContentType(File f) throws Exception {
		
//		return "application/octet-stream";  // 此行不再细分是否为图片,全部作为application/octet-stream 类型
		ImageInputStream imagein = ImageIO.createImageInputStream(f);
		if (imagein == null) {
			return "application/octet-stream";
		}
		Iterator<ImageReader> it = ImageIO.getImageReaders(imagein);
		if (!it.hasNext()) {
			imagein.close();
			return "application/octet-stream";
		}
		imagein.close();
		return "image/" + it.next().getFormatName().toLowerCase();//将FormatName返回的值转换成小写,默认为大写

	}
    //把文件转换成字节数组
	private byte[] getBytes(File f) throws Exception {
		FileInputStream in = new FileInputStream(f);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		byte[] b = new byte[1024];
		int n;
		while ((n = in.read(b)) != -1) {
			out.write(b, 0, n);
		}
		in.close();
		return out.toByteArray();
	}
	//添加结尾数据
	private void paramsEnd() throws Exception {
		ds.writeBytes("--" + boundary + "--" + "\r\n");
		ds.writeBytes("\r\n");
	}
	// 对包含中文的字符串进行转码,此为UTF-8。服务器那边要进行一次解码
    private String encode(String value) throws Exception{
    	return URLEncoder.encode(value, "UTF-8");
    }
	public static void main(String[] args) throws Exception {
		HttpPostUtil u = new HttpPostUtil("http://localhost:3000/up_load");
		u.addFileParameter("img", new File(
				"D:\\素材\\圆月.jpg"));
		u.addTextParameter("text", "中文");
		byte[] b = u.send();
		String result = new String(b);
		System.out.println(result);

	}

}

 

 

后台使用ruby进行接收

ruby代码如下:

require "cgi"
class UpLoadController < ApplicationController
	protect_from_forgery :except=>:index
	
	def index
		img = params[:img]
		if img.kind_of? String
			logger.debug "img string : #{img}"
		else
			logger.debug "Content-Type:#{img.content_type}"
			logger.debug "or:#{CGI.unescape(img.original_filename)}"
		end
		text = params[:text]
		logger.debug "text:#{CGI.unescape(text)}"
		render :text=>"OK"
	end
end

 

日志输入如下:

 Content-Type:image/jpeg
or:圆月.jpg
text:中文

 

如果不把中文转成UTF-8的格式进行传输,则后台显示中文乱码。

同样,如果其他参数包含中文,则也应当先转码。

当然,具体什么编码要和后台接收的编码一致。

 

 

 另外附上c#的代码,因为对c#不太熟悉,但代码测试基本可以实现了。

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Drawing.Imaging;

namespace MyThreading
{
    class HttpPostUtil
    {
        string boundary = "------httpost123";
        string url;
        byte[] bs= new byte[0];
        Encoding encoder = Encoding.UTF8;
        public HttpPostUtil(string url)
        {
            this.url = url;
        }
        public byte[] Send()
        {
            WebClient myWebClient = new WebClient();
            myWebClient.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
            EndData();
            return myWebClient.UploadData(url,bs); 
        }
        public void ClearData()
        {
            Array.Clear(bs, 0, bs.Length);
        }
        public void AddTextParameter(string name, string value)
        {
            StringBuilder s = new StringBuilder();
            s.Append("--").Append(boundary).Append("\r\n");
            s.Append("Content-Disposition:  form-data;  name=\"" + name + "\"\r\n");
            s.Append("\r\n");
            s.Append(value).Append("\r\n");
            AppendString(s.ToString());
        }
        public void AddFileParameter(string name, FileInfo file)
        {
            StringBuilder s = new StringBuilder();
            s.Append("--").Append(boundary).Append("\r\n");
            s.Append("Content-Disposition:  form-data;  name=\"" + name + "\";  filename=\"" + file.Name + "\"\r\n");
            s.Append("Content-Type: " + GetContentType(file) + "\r\n");
            s.Append("\r\n");
            AppendString(s.ToString());
            AppendBytes(GetFileBytes(file));
            AppendString("\r\n");     
        }
        byte[] GetFileBytes(FileInfo f)
        {
            FileStream fs = new FileStream(f.FullName, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);
            byte[] b = br.ReadBytes((int)f.Length);
            br.Close();
            fs.Close();
            return b;
        }
        string GetContentType(FileInfo f)
        {
          // return "application/octet-stream";  //取消注释此行,则不再区分是否为图片
            System.Drawing.Image image = null;
            FileStream fs = new FileStream(f.FullName, FileMode.Open);
            try
            {
                image = System.Drawing.Image.FromStream(fs);
            }
            catch (ArgumentException)
            {
                // 文件无法被解析为一个图片
                return "application/octet-stream";
            }
            finally 
            {
                fs.Close();
            }
            return  GetFormat(image.RawFormat);
        }
        string GetFormat(ImageFormat imf) 
        {
            if (ImageFormat.Bmp.Equals(imf))
            {
                return "image/bmp";
            }
            if (ImageFormat.Jpeg.Equals(imf)) 
            {
                return "image/jpeg";
            }
            if (ImageFormat.Png.Equals(imf)) 
            {
                return "image/png";
            }
            return "application/octet-stream";
        }
        void AppendBytes(byte[] bytes) 
        {
            byte[] newByte = new byte[bs.Length + bytes.Length];
            Array.Copy(bs, 0, newByte, 0, bs.Length);
            Array.Copy(bytes, 0, newByte, bs.Length, bytes.Length);
            bs = newByte;
        }
        void AppendString(string value)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(value);
            AppendBytes(bytes);
        }
        void EndData()
        {
            StringBuilder s = new StringBuilder();
            s.Append("--").Append(boundary).Append("--\r\n");
            s.Append("\r\n");
            AppendString(s.ToString());
        }
    }
}

在我本机测试,中文没有出现乱码。

c#调用方法:

 

           string url = "http://localhost:3000/up_load";
           string imgPath = @"D:\素材\圆月.jpg";
           HttpPostUtil hp = new HttpPostUtil(url);
           hp.AddTextParameter("text","中文");
           hp.AddFileParameter("img",new FileInfo(imgPath));
           byte[] b = hp.Send();
            Console.WriteLine(Encoding.UTF8.GetString(b));

 

如有错误,请补充下。

分享到:
评论
4 楼 酷的飞上天空 2015-04-20  
haiyinlong 写道
private File value;  
获得到对象中显示乱码? 是什么情况?
如果我只想传递 byte[]  怎么传递?

例如: 
     String name;
     byte[] img;
     int  age ;

    要通过 httpUrlcontent 传递应怎么写?  求帮助.  

    haiyinlong@yeah.net 


如果是传递小文件,比如头像一类的,可以直接按普通字符串的方式。
具体是把文件读取到内存保存为字节数组,然后使用base64把字节数组变成字符串,后台收到这个字符串后再对base64字符串解码。
3 楼 haiyinlong 2014-08-27  
private File value;  
获得到对象中显示乱码? 是什么情况?
如果我只想传递 byte[]  怎么传递?

例如: 
     String name;
     byte[] img;
     int  age ;

    要通过 httpUrlcontent 传递应怎么写?  求帮助.  

    haiyinlong@yeah.net 
2 楼 SunshineMe 2014-04-02  
帅哥,说实话有点没看懂,求讲解!先教教我如何使用呗!!!
1 楼 wangsong76 2011-07-05  
相当不错!
适合android上实现附件上传功能。

不过需要改一下:
Map<String, File> fileparams = new HashMap<String, File>();  

改为:
ArrayList<FileEntity> fileparams= new ArrayList<FileEntity>();

添加一个内部类
class FileEntity{
	private String name;
	private File value;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public File getValue() {
		return value;
	}
	public void setValue(File value) {
		this.value = value;
	}
	public FileEntity(String name, File value) {
		super();
		this.name = name;
		this.value = value;
	}
}

对应的,取file参数的时候,也需要改一下:
//文件数据
	private void writeFileParams() throws Exception {
		
		for (FileEntity fileEntity:fileparams) {
			String name = fileEntity.getName();
			File value = fileEntity.getValue();
			ds.writeBytes("--" + boundary + "\r\n");
			ds.writeBytes("Content-Disposition: form-data; name=\"" + name
					+ "\"; filename=\"" + encode(value.getName()) + "\"\r\n");
			ds.writeBytes("Content-Type: " + getContentType(value) + "\r\n");
			ds.writeBytes("\r\n");
			ds.write(getBytes(value));
			ds.writeBytes("\r\n");
		}
	}

相关推荐

    Java利用HttpURLConnection发送post请求上传文件.docx

    Java利用HttpURLConnection发送post请求上传文件

    HttpURLConnection

    HttpURLConnection的demo,里面有网络请求get post 上传文件,下载文件,介绍HttpURLConnection这个类整体流程是怎样使用的,方便初学者学习

    HttpAgent:Android网络请求框架,可以自定义网络引擎,支持GET,POST,文件上传,可以监听文件上传进度

    HttpAgent Android网络请求框架,可以自定义网络引擎,支持GET,POST,文件上传,文件下载,可以监听文件下载上传进度。本库内部基于OkHttp和HttpUrlConnection实现了两套网络请求逻辑,使用者可以根据自己的需要...

    HttpSend请求,不得少于十一字

    封装了HttpURLConnection,便于http的post和get请求,可以进行http的文件上传和下载,https免验证请求,请求日志

    Android 上传文件工具类

    Android 上传文件至服务器和下载文件至本地,亲测有效,只需传入相关参数即可。/** * android上传文件到服务器 * * @param file * 需要上传的文件 * @param RequestURL * 请求的rul * @return 返回...

    Android网络请求之OkHttp

    OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题 ...

    OkHttp3入门-手把手教你如何用

    OkHttp3入门准备工作添加依赖设置网络权限创建简单创建四步走异步请求同步请求中止请求具体介绍GET请求POST请求下载(保存到本地)简单的下载请求(示例)准备工作显示下载进度上传文件上传文件上传(简单示例)涉及...

    精通ANDROID 3(中文版)1/2

    11.1.2 将HttpClient用于HTTPPOST请求(多部分POST请求示例)  11.1.3 SOAP、JSON和XML分析程序  11.1.4 处理异常  11.1.5 解决多线程问题  11.1.6 有趣的超时  11.1.7 使用HttpURLConnection  11.1.8 ...

    精通Android 3 (中文版)2/2

    11.1.2 将HttpClient用于HTTPPOST请求(多部分POST请求示例)  11.1.3 SOAP、JSON和XML分析程序  11.1.4 处理异常  11.1.5 解决多线程问题  11.1.6 有趣的超时  11.1.7 使用HttpURLConnection  11.1.8 ...

    Android典型技术模块开发详解

    11.4.2 HttpPost请求 11.5 本章小结 第12章 数据解析 12.1 XML 12.1.1 DOM 12.1.2 SAX 12.1.3 PULL 12.2 JSON格式 12.2.1 基本类型 12.2.2 数组和集合 12.2.3 类对象 12.3 JSON解析 12.4 Gson 12.4.1 简单对象类型...

    Jboot微服务框架-其他

    16、Http客户端(包含了get、post请求,文件上传和下载等)  httpUrlConnection  okHttp  httpClient 17、分布式下的微信和微信第三方 18、自定义序列化组件 19、事件机制 20、代码生成器 21、等等     Jboot...

Global site tag (gtag.js) - Google Analytics