title: Java - Using HttpURLConnection to make POST request and send image to server
body: <p>I'm working on a team software project that involves designing a client for a server-based AI called SeeFood. You can send it a picture, and it will tell you whether or not the picture has food in it. We currently have a python script deployed to the server that accepts Http POST requests and calls the AI with an image that it is given. You can access that at 34.236.92.140. </p>
  
  <p>The challenge I'm facing right now is getting my Java client to be able to send an image to the server, have it analyzed, and get a response back. I've been trying different things, including the Apache HttpComponents library, but I'm constantly getting this response code from the server when I run the code:</p>
  
  <pre><code>400 BAD REQUEST
  Server: Apache/2.4.27 (Amazon) PHP/5.6.30 mod_wsgi/3.5 Python/2.7.12
  Connection: close
  Content-Length: 192
  Date: Fri, 17 Nov 2017 16:11:28 GMT
  Content-Type: text/html; charset=UTF-8
  </code></pre>
  
  <p>Judging by research done on HTTP code 400, the server doesn't like how I've formatted the POST request. Does anyone have experience with HTTP servers and sending images via POST? Again, you can try out the server side application at 34.236.92.140. I'll also include the Java client and Python server code.</p>
  
  <p>Java Client (relevant code under the exportImages and readResultsToString methods):</p>
  
  <pre><code>package javaapplication12;
  import java.io.*;
  import java.net.HttpURLConnection;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.nio.channels.Channels;
  import java.nio.channels.FileChannel;
  import java.nio.channels.WritableByteChannel;
  import java.util.*;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  import javafx.application.*;
  import static javafx.application.Application.launch;
  import javafx.event.*;
  import javafx.geometry.*;
  import javafx.scene.*;
  import javafx.scene.control.*;
  import javafx.scene.layout.*;
  import javafx.stage.*;
  
  public class UserInterface extends Application {
      private List&lt;File&gt; _images;
      /**
       * @param args the command line arguments
       */
  public static void main (String[] args) {
      System.setProperty("java.net.preferIPv4Stack" , "true");
      launch (args);
  }
  
  @Override
  public void start (Stage primaryStage) {
      final FileChooser fc=new FileChooser ();
      primaryStage.setTitle ("SeeFood AI User Interface");
      Button imageButton=new Button ("Import Images");
      Button exportButton=new Button ("Send Images to SeeFood");
      //When image button is pressed, a FileChooser should load up and add all selected images to a list
      imageButton.setOnAction ((ActionEvent event) -&gt; {
          _images=fc.showOpenMultipleDialog (primaryStage);
          if (_images!=null) {
              int i=0;
              //loop to verify that all selected images are added
              for (File file:_images) {
                  System.out.println ("image "+i);
                  i++;
              }
          }
      });
  
      exportButton.setOnAction ((ActionEvent event) -&gt; {
          try {
              exportImages();
          } catch (IOException ex) {
              Logger.getLogger(UserInterface.class.getName()).log(Level.SEVERE, null, ex);
          }
      });
      final GridPane inputGridPane=new GridPane ();
  
      GridPane.setConstraints (imageButton,0,0);
      GridPane.setConstraints (exportButton,0,1);
      inputGridPane.setHgap (6);
      inputGridPane.setVgap (6);
      inputGridPane.getChildren ().addAll (imageButton, exportButton);
  
      final Pane rootGroup=new VBox (12);
      rootGroup.getChildren ().addAll (inputGridPane);
      rootGroup.setPadding (new Insets (12,12,12,12));
      primaryStage.setScene (new Scene (rootGroup));
      primaryStage.show ();
  }
  /**
   * Sends one or more images to SeeFood via HTTP POST.
   * @throws MalformedURLException
   * @throws IOException 
   */
  private void exportImages() throws MalformedURLException, IOException{       
      //InetAddress host=InetAddress.getByName(_ip);
  //        System.out.println(InetAddress.getByName(_ip));
      URL url=new URL("http://34.236.92.140");
      HttpURLConnection con=(HttpURLConnection) url.openConnection();
      String output;
  
      con.setRequestMethod("POST");
      con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
      con.setRequestProperty("Content-Type", "multipart/form-data");
  
      FileChannel in;
      WritableByteChannel out;
  
      con.setDoOutput(true);  //this must be set to true in order to work
      con.setDoInput(true);
  
      for(File file:_images){
          in=new FileInputStream(file).getChannel();
          out=Channels.newChannel(con.getOutputStream());
  
          in.transferTo(0, file.length(), out);
  
          StringBuilder builder = new StringBuilder();
          builder.append(con.getResponseCode())
                 .append(" ")
                 .append(con.getResponseMessage())
                 .append("\n");
  
          Map&lt;String, List&lt;String&gt;&gt; map = con.getHeaderFields();
          for (Map.Entry&lt;String, List&lt;String&gt;&gt; entry : map.entrySet()){
              if (entry.getKey() == null) 
                  continue;
              builder.append( entry.getKey())
                     .append(": ");
  
              List&lt;String&gt; headerValues = entry.getValue();
              Iterator&lt;String&gt; it = headerValues.iterator();
              if (it.hasNext()) {
                  builder.append(it.next());
  
                  while (it.hasNext()) {
                      builder.append(", ")
                             .append(it.next());
                  }
              }
  
              builder.append("\n");
          }
  
          System.out.println(builder);
           //Output the result from SeeFood
          //Later on, this result should be stored for each image
          output=readResultsToString(con);
  
  
          if(output!=null){
              System.out.println(output);
          } else {
              System.out.println("There was an error in the connection.");
          }
          in.close();
          out.close();
      }       
      con.disconnect();
  }
  
  /**
   * Helper method to exportImages(). Should get response from server
   * and append contents to string.
   * @param con - the active http connection
   * @return response from the server
   */
  private String readResultsToString(HttpURLConnection con){
      String result = null;
      StringBuffer sb = new StringBuffer();
      InputStream is = null;
  
      try {          
          is=new BufferedInputStream(con.getInputStream());
          BufferedReader br=new BufferedReader(new InputStreamReader(is));
          String inputLine="";
          while((inputLine=br.readLine())!=null){
              sb.append(inputLine);
          }
          result=sb.toString();
      } catch (IOException ex) {
          Logger.getLogger(UserInterface.class.getName()).log(Level.SEVERE, null, ex);
      } finally {
          if(is!=null){
              try {
                  is.close();
              } catch (IOException ex) {
                  Logger.getLogger(UserInterface.class.getName()).log(Level.SEVERE, null, ex);
              }
          }
      }
      return result;
  }
  }
  </code></pre>
  
  <p>Python server:</p>
  
  <pre><code>from flask import Flask, send_from_directory, request
  from werkzeug.utils import secure_filename
  import argparse
  import numpy as np
  import tensorflow as tf
  from PIL import Image
  import sys
  
  app = Flask(__name__)
  
  '''
  method for uploading files to the server 
  via http POST request
  '''
  @app.route('/upload', methods=['GET', 'POST'])
  def upload_file():
      if request.method == 'POST':
          f = request.files['file']
          f.save(secure_filename(f.filename))
          print f.filename
          score = ai_call(f.filename)
          #save file in location based on score
          return score
  
      return '''
  &lt;!doctype html&gt;
  &lt;title&gt;Upload new File&lt;/title&gt;
  &lt;h1&gt;Upload new File&lt;/h1&gt;
  &lt;form method=post enctype=multipart/form-data&gt;
    &lt;p&gt;&lt;input type=file name=file&gt;
       &lt;input type=submit value=Upload&gt;
  &lt;/form&gt;
  '''
  '''
  method for returning files from the server based on filename
  '''
  @app.route('/download/&lt;file_name&gt;')
  def get_file(file_name):
      return app.send_static_file(file_name)
  
  '''
  index page
  needs to be motifed to return default images
  '''
  @app.route('/')
  def index():
      find_food
      return 'Hello World'
  
  """
  A script to ask SeeFood if it sees food in the image at 
  path specified by the command line argument.
  """
  def ai_call(system_arg):
      #parser = argparse.ArgumentParser(description="Ask SeeFood if there is 
  food in the image provided.")
  #parser.add_argument('image_path', help="The full path to an image file stored on disk.")
  #args = parser.parse_args()
  
  # The script assumes the args are perfect, this will crash and burn otherwise.
  
  ###### Initialization code - we only need to run this once and keep in memory.
  sess = tf.Session()
  saver = tf.train.import_meta_graph('saved_model/model_epoch5.ckpt.meta')
  saver.restore(sess, tf.train.latest_checkpoint('saved_model/'))
  graph = tf.get_default_graph()
  x_input = graph.get_tensor_by_name('Input_xn/Placeholder:0')
  keep_prob = graph.get_tensor_by_name('Placeholder:0')
  class_scores = graph.get_tensor_by_name("fc8/fc8:0")
  ######
  
  # Work in RGBA space (A=alpha) since png's come in as RGBA, jpeg come in as RGB
  # so convert everything to RGBA and then to RGB.
  #image_path = args.image_path
  image_path = system_arg
  image = Image.open(image_path).convert('RGB')
  image = image.resize((227, 227), Image.BILINEAR)
  img_tensor = [np.asarray(image, dtype=np.float32)]
  print 'looking for food in '+ image_path
  
  #Run the image in the model.
  scores = sess.run(class_scores, {x_input: img_tensor, keep_prob: 1.})
  print scores
  # if np.argmax = 0; then the first class_score was higher, e.g., the model sees food.
  # if np.argmax = 1; then the second class_score was higher, e.g., the model does not see food.
  if np.argmax(scores) == 1:
      print "No food here... :disappointed: "
  else:
      print "Oh yes... I see food! :D"
  return str(scores)
  
  if __name__ == '__main__':
  app.debug = True
  app.run()
  </code></pre>
  
  <p>Any help you can offer is appreciated. Thank you in advance.</p>