Saturday, November 21, 2015

Multipart form upload utility

Often I find the developers struggling how to upload a file using API on server. A simple utility class in Java which helps uploading file along with other data in standard multi-part format to any given API.

package util.net;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

/** 
 * Class used to send the Multi-part data on the server. 
 */
public class MultipartUtility {

 private static final String LINE_FEED = "\r\n";
 private static final String CHARSET = "UTF-8";
 public static final String CSQ = "--";
 private final String BOUNDARY;
 private HttpURLConnection mHttpURLConnection;
 private OutputStream mOutputStream;
 private PrintWriter mWriter;

 static {
  // Doing default HTTPS initialization
  try {
   SSLContext sslContext;
   sslContext = SSLContext.getInstance("TLSv1.2");
   sslContext.init(null, new X509TrustManager[] {
    new X509TrustManager() {@Override
     public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

     @Override
     public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

     @Override
     public X509Certificate[] getAcceptedIssuers() {
      return null;
     }
    }
   }, null);
   HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
   HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {@Override public boolean verify(String hostname, SSLSession session) {
     return true;
    }
   });
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }

 /**     
  * This constructor initializes a new HTTP POST request with content type     
  * is set to multipart/form-data     
  *     
  * @param requestURL The URL where data to be posted     
  * @throws IOException Thrown while creating instances.     
  */
 public MultipartUtility(String requestURL) throws IOException {
  // creates a unique boundary based on time stamp        
  BOUNDARY = "----" + System.currentTimeMillis();
  URL url = new URL(requestURL);
  mHttpURLConnection = (HttpURLConnection) url.openConnection();
  mHttpURLConnection.setUseCaches(false);
  mHttpURLConnection.setDoOutput(true); // indicates POST method        mHttpURLConnection.setDoInput(true);
  mHttpURLConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
  mHttpURLConnection.setRequestProperty("User-Agent", "Java");
  mOutputStream = mHttpURLConnection.getOutputStream();
  mWriter = new PrintWriter(new OutputStreamWriter(mOutputStream, CHARSET), true);
 }

 /**     
  * Adds a form field to the request     
  *     
  * @param name  field name     
  * @param value field value     
  */
 public void addFormField(String name, String value) {
  mWriter.append(CSQ).append(BOUNDARY).append(LINE_FEED);
  mWriter.append("Content-Disposition: form-data; name=\"").append(name).append("\"").append(LINE_FEED);
  mWriter.append("Content-Type: text/plain; charset=" + CHARSET).append(LINE_FEED);
  mWriter.append(LINE_FEED);
  mWriter.append(value).append(LINE_FEED);
  mWriter.flush();
 }

 /**
  * Adds a upload file section to the request
  *
  * @param fieldName  name attribute in 
  * @param uploadFile a File to be uploaded
  * @throws IOException Thrown while adding file
  */
 public void addFilePart(String fieldName, File uploadFile) throws IOException {
  String fileName = uploadFile.getName();
  mWriter.append(CSQ).append(BOUNDARY).append(LINE_FEED);
  mWriter.append("Content-Disposition: form-data; name=\"").append(fieldName).append("\"; filename=\"").append(fileName).append("\"").append(LINE_FEED);
  mWriter.append("Content-Type: ").append(URLConnection.guessContentTypeFromName(fileName)).append(LINE_FEED);
  mWriter.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
  mWriter.append(LINE_FEED);
  mWriter.flush();
  FileInputStream inputStream = new FileInputStream(uploadFile);
  byte[] buffer = new byte[4096];
  int bytesRead;
  while ((bytesRead = inputStream.read(buffer)) != -1) {
   mOutputStream.write(buffer, 0, bytesRead);
  }
  mOutputStream.flush();
  inputStream.close();
  mWriter.append(LINE_FEED);
  mWriter.flush();
 }

 /**
  * Adds a header field to the request.
  *
  * @param name  - name of the header field
  * @param value - value of the header field
  */
 public void addHeaderField(String name, String value) {
  mWriter.append(name).append(": ").append(value).append(LINE_FEED);
  mWriter.flush();
 }

 /**
  * Completes the request and receives response from the server.
  *
  * @return a list of Strings as response in case the server returned
  * status OK, otherwise an exception is thrown.
  * @throws IOException Thrown while submitting Multipart form data.
  */
 public List < String > finish() throws IOException {
  List < String > response = new ArrayList < > ();
  mWriter.append(LINE_FEED).flush();
  mWriter.append(CSQ).append(BOUNDARY).append(CSQ).append(LINE_FEED);
  mWriter.close();
  // checks server's status code first
  int status = mHttpURLConnection.getResponseCode();
  if (status == HttpURLConnection.HTTP_OK) {
   BufferedReader reader = new BufferedReader(new InputStreamReader(mHttpURLConnection.getInputStream()));
   String line;
   while ((line = reader.readLine()) != null) {
    response.add(line);
   }
   reader.close();
   mHttpURLConnection.disconnect();
  } else {
   throw new IOException("Server returned non-OK status: " + status);
  }
  return response;
 }
}

Thank you http://hilite.me for helping the source code pretty formatting.