项目中须要生成图像文件,并上传到第三方平台。第三方平台提供的接口是http接口。并提供了比较全面的接口文档。java
private static final String username = "admin"; private static final String password = "123456"; public static void create(){ String auth = encodeBase64(username+":"+password); HttpClient httpClient = HttpClients.createDefault(); String url = "http://yourdomain/example-url"; HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Authorization", "Basic " + auth); //添加认证消息头 try { MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); //添加要上传的文件 multipartEntityBuilder.addBinaryBody("FILE", new File("E://111.jpg")).setMode(HttpMultipartMode.RFC6532); //传入参数 multipartEntityBuilder.addPart("IMAGE_TYPE", new StringBody("111",ContentType.APPLICATION_JSON)); multipartEntityBuilder.addPart("PAGE_NUM", new StringBody("1",ContentType.APPLICATION_JSON)); multipartEntityBuilder.addPart("CREATE_TIME", new StringBody("2018-3-8 1:38:56",ContentType.APPLICATION_JSON)); httpPost.setEntity(multipartEntityBuilder.build()); HttpResponse httpResponse = httpClient.execute(httpPost); int code = httpResponse.getStatusLine().getStatusCode(); if (code == 200) { String strResult = EntityUtils.toString(httpResponse.getEntity()); System.out.println(strResult); } else{ HttpEntity httpEntity = httpResponse.getEntity(); String content = EntityUtils.toString(httpEntity); System.out.println(content); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { create(); }
文档中基于httpClient的方式进行调用,而且上传文件的作法都是上传本地File。
项目中已经使用了比较老的commons-httpclient。apache
并且个人文件是已经生成好在内存中的byte[]数据。比较直接的作法是先把byte[]数据保存到一个临时目录。在经过new File读取文件并上传。可是做为一个强迫症,这样画蛇添足是不能接受的。因此须要探索下commons-httpclient如何直接上传byte[]格式的文件。dom
固然首先是要看下commons-httpclient如何上传文件。ide
刚开始找到一个例子是:svn
MultipartPostMethod filePost = new MultipartPostMethod(targetURL); filePost.addParameter( "fileName" , targetFilePath); HttpClient client = new HttpClient(); // 因为要上传的文件可能比较大 , 所以在此设置最大的链接超时时间 client.getHttpConnectionManager(). getParams().setConnectionTimeout(5000); int status = client.executeMethod(filePost);
可是项目中使用的是commons-httpclient-3.0,MultipartPostMethod 已经被废弃。并且也是直接addParameter( "fileName" , targetFilePath);
看了下源码很差调整。函数
最后找到官网的例子(地址:http://svn.apache.org/viewvc/httpcomponents/oac.hc3x/trunk/src/examples/MultipartFileUploadApp.java?view=co)post
对了,commons-httpclient已经中止维护,推荐使用httpClient。ui
示例代码有些调整。this
PostMethod filePost = new PostMethod(targetURL); File targetFile = new File("E:/111.jpg"); try { System.out.println("Uploading " + targetFile.getName() + " to " + targetURL); Part[] parts = { new FilePart(targetFile.getName(), targetFile) }; filePost.setRequestEntity( new MultipartRequestEntity(parts, filePost.getParams()) ); HttpClient client = new HttpClient(); client.getHttpConnectionManager(). getParams().setConnectionTimeout(5000); int status = client.executeMethod(filePost); if (status == HttpStatus.SC_OK) { System.out.println( "Upload complete, response=" + filePost.getResponseBodyAsString() ); } else { System.out.println( "Upload failed, response=" + HttpStatus.getStatusText(status) ); } } catch (Exception ex) { System.out.println("ERROR: " + ex.getClass().getName() + " " + ex.getMessage()); ex.printStackTrace(); } finally { filePost.releaseConnection(); }
能够看到主要参数就在parts 里面。其中FilePart就是要上传的文件参数。FilePart的构造函数第一个参数为参数名,第二个是文件对象。
查看FilePart源码,该构造函数为:url
public FilePart(String name, File file) throws FileNotFoundException { this(name, ((PartSource) (new FilePartSource(file))), null, null); }
能够看到,将File对象封装成FilePartSource。
另外FilePart的sendData方法以下
protected void sendData(OutputStream out) throws IOException { LOG.trace("enter sendData(OutputStream out)"); if (lengthOfData() == 0L) { LOG.debug("No data to send."); return; } byte tmp[] = new byte[4096]; InputStream instream = source.createInputStream(); int i; try { while ((i = instream.read(tmp)) >= 0) out.write(tmp, 0, i); } finally { instream.close(); } }
能够猜到这个就是拿到http连接的OutputStream ,往里面写数据。写的是从source拿到的InputStream 里面的内容。这个source就是前面的FilePartSource。
查看FilePartSource源码,至关因而对File的一层封装。主要方法就是实现的接口PartSource的几个方法。
public interface PartSource { public abstract long getLength(); public abstract String getFileName(); public abstract InputStream createInputStream() throws IOException; }
联想到sendData方法里面调用的source.createInputStream();
若是这个地方获取的InputStream 若是不是FileInputStream 而是ByteArrayInputStream不就能够了吗?也就是说本身写个BytesFilePartSource 类实现PartSource接口,可是不封装File,而是封装byte[]不就能够了。而后构建FilePart时传入本身写的BytesFilePartSource 。根据FilePartSource写了本身的BytesFilePartSource 。
public class BytesFilePartSource implements PartSource { private byte[] bytes; private String fileName; public BytesFilePartSource(String fileName, byte[] bytes) throws FileNotFoundException { this.fileName = fileName; this.bytes = bytes; } @Override public long getLength() { if (bytes != null) return bytes.length; else return 0L; } @Override public String getFileName() { return fileName != null ? fileName : "noname"; } @Override public InputStream createInputStream() throws IOException { if (bytes != null) return new ByteArrayInputStream(bytes); else return new ByteArrayInputStream(new byte[0]); } }
其实找找代码发现jar中已经有一个类实现了这样的功能:ByteArrayPartSource。因此使用这个类就好了,就不要重复造轮子了。
主要代码以下:
HttpClient httpClient = new HttpClient(); httpClient.getHttpConnectionManager(). getParams().setConnectionTimeout(5000); PostMethod postMethod = new PostMethod(url); // 添加认证消息头 postMethod.setRequestHeader("Authorization", "Basic " + auth); try { Part[] parts = { new FilePart("FILE", new ByteArrayPartSource("111.jpg", imageBytes)), new StringPart("IMAGE_TYPE", imageType, "GBK"), new StringPart("SEQ_NUMBER", pageNum + "", "GBK"), new StringPart("PAGE_NUM", time, "GBK"), new StringPart("CREATE_TIME", time, "GBK") }; postMethod.setRequestEntity(new MultipartRequestEntity(parts, postMethod.getParams())); int status = httpClient.executeMethod(postMethod); log.info("status:" + status + "Result:" + strResult); } catch (Exception e) { e.printStackTrace(); contentId = ""; } finally { postMethod.releaseConnection(); }
最后,其实最新的httpclient也是支持直接发送二进制数据的,不必先保存数据到磁盘再读取。