星期一, 12月 02, 2013

[Java] 如何將上傳streaming透過Http Client再上傳到另外一個Server

目前有一個需求是要將上傳api拿到的Stream再透過REST API上傳至另一個儲存空間。
採用的架構是透過Java Jersey + Apache HttpClient 4.3

測試範例如下:



package test;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;

import java.security.cert.X509Certificate;

import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

@Path("/vfs")
public class VFS {

	@POST
	@Path("/upload")
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	public String uploadObject(
			@Context HttpServletRequest request,
			@Context HttpServletResponse response, 
			InputStream inputStream)
			throws IOException, JSONException {

		String resp = null;
		
	
		String uploadAPI = 
				"http://localhost:8080/JerseySample/services/vfs/internal_upload";
		
		
		// request
		URI uploadURI = null;
		CloseableHttpClient httpClient = null;
		HttpPost httpPost = null;
		InputStreamEntity requestEntity = null;

		// response
		InputStream responseInputStream = null;
		HttpEntity httpEntity = null;
		HttpResponse httpResponse = null;

		// setting
		uploadURI = URI.create(uploadAPI);
		
//		httpClient = HttpClients.createDefault();
		httpClient = getHttpClient();//for https

		httpPost = new HttpPost();
		httpPost.setURI(uploadURI);
		
		String orginalContentType = request.getHeader("Content-Type");
		//important!!
		System.out.println("orginalContentType:" + orginalContentType);
		httpPost.addHeader("Content-Type", orginalContentType);
		
		int contentLen = request.getContentLength();
		System.out.println("contentLen:" + contentLen);
		requestEntity = new InputStreamEntity(
				inputStream,
				contentLen);
		httpPost.setEntity(requestEntity);

		try {
			// do request
			httpResponse = httpClient.execute(httpPost);
			
			// get response entity
			httpEntity = httpResponse.getEntity();
			// responseInputStream = httpEntity.getContent();

			System.out.println("status code:"
					+ httpResponse.getStatusLine().getStatusCode());

			resp = EntityUtils.toString(httpEntity);

		} finally {
			httpPost.releaseConnection();
			IOUtils.closeQuietly(responseInputStream);
			EntityUtils.consumeQuietly(httpEntity);
		}

		return resp;
	}

	public static CloseableHttpClient getHttpClient() {
		SSLContextBuilder builder = new SSLContextBuilder();
		try {
			builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
					builder.build(), new X509HostnameVerifier() {
						@Override
						public boolean verify(String arg0, SSLSession arg1) {
							return true;
						}

						@Override
						public void verify(String arg0, SSLSocket arg1)
								throws IOException {
						}

						@Override
						public void verify(String arg0, X509Certificate arg1)
								throws SSLException {
						}

						@Override
						public void verify(String arg0, String[] arg1,
								String[] arg2) throws SSLException {
						}
					});
			return HttpClients.custom().setSSLSocketFactory(sslsf).build();
		} catch (NoSuchAlgorithmException | KeyStoreException
				| KeyManagementException e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
	}

	@POST
	@Path("/internal_upload")
	@Consumes(MediaType.MULTIPART_FORM_DATA)
	public String uploadObject(
			@Context HttpServletRequest request,
			@Context HttpServletResponse response) throws IOException,
			JSONException {

		JSONObject respJSON = null;
		System.out.println("upload start");
		try {

			// create a new file upload handler
			ServletFileUpload upload = new ServletFileUpload();
			// parse request
			System.out.println("parse request");
			FileItemIterator iter = (FileItemIterator) upload
					.getItemIterator(request);

			while (iter.hasNext()) {

				FileItemStream item = (FileItemStream) iter.next();

				String nameOfField = item.getFieldName();
				InputStream inputStream = null;
				if (!item.isFormField()) {
					// file binary
					System.out.println("File field '" + nameOfField
							+ "' with file name '" + item.getName()
							+ "' detected.");
					// process the input stream
					inputStream = item.openStream();// input stream of file from
													// client

				} else {
					inputStream = item.openStream();
					String valOfField = Streams.asString(inputStream, "UTF-8");
					// form field
					System.out.println("String field " + nameOfField
							+ " with field value " + valOfField + " detected.");
				}
			}
		} catch (FileUploadException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			System.out.println(String.format("Cannot upload file to: %s",
					e.getMessage()));
		}

		respJSON = new JSONObject();
		respJSON.put("statuscode", 200);

		System.out.println("upload end");

		return respJSON.toString();
	}

}


可用REST client 測試upload API,之後upload api會透過Httpclient把Stream再送到internal_upload API。 先前遇到internal_upload無法正確解析boundary,關鍵在於upload api並未將request content-type送對,請參考下面程式碼:
String orginalContentType = request.getHeader("Content-Type");httpPost.addHeader("Content-Type", orginalContentType);

透過boundary才有辦法正確解析multipart/form-data。以下是透過REST Client所擷取的畫面

沒有留言:

張貼留言

留個話吧:)

其他你感興趣的文章

Related Posts with Thumbnails