private void writeToFromStream(OutputStream os, RequestParams.StreamWrapper entry)
      throws IOException {

    // Send the meta data.
    writeMetaData(os, entry.name, entry.contentType);

    int bytesRead;

    // Upload the file's contents in Base64.
    Base64OutputStream bos = new Base64OutputStream(os, Base64.NO_CLOSE | Base64.NO_WRAP);

    // Read from input stream until no more data's left to read.
    while ((bytesRead = entry.inputStream.read(buffer)) != -1) {
      bos.write(buffer, 0, bytesRead);
    }

    // Close the Base64 output stream.
    AsyncHttpClient.silentCloseOutputStream(bos);

    // End the meta data.
    endMetaData(os);

    // Close input stream.
    if (entry.autoClose) {
      // Safely close the input stream.
      AsyncHttpClient.silentCloseInputStream(entry.inputStream);
    }
  }
  private void writeToFromFile(OutputStream os, RequestParams.FileWrapper wrapper)
      throws IOException {

    // Send the meta data.
    writeMetaData(os, wrapper.file.getName(), wrapper.contentType);

    int bytesRead;
    long bytesWritten = 0, totalSize = wrapper.file.length();

    // Open the file for reading.
    FileInputStream in = new FileInputStream(wrapper.file);

    // Upload the file's contents in Base64.
    Base64OutputStream bos = new Base64OutputStream(os, Base64.NO_CLOSE | Base64.NO_WRAP);

    // Read from file until no more data's left to read.
    while ((bytesRead = in.read(buffer)) != -1) {
      bos.write(buffer, 0, bytesRead);
      bytesWritten += bytesRead;
      progressHandler.sendProgressMessage(bytesWritten, totalSize);
    }

    // Close the Base64 output stream.
    AsyncHttpClient.silentCloseOutputStream(bos);

    // End the meta data.
    endMetaData(os);

    // Safely close the input stream.
    AsyncHttpClient.silentCloseInputStream(in);
  }
  @Override
  public void writeTo(final OutputStream out) throws IOException {
    if (out == null) {
      throw new IllegalStateException("Output stream cannot be null.");
    }

    // Record the time when uploading started.
    long now = System.currentTimeMillis();

    // Use GZIP compression when sending streams, otherwise just use
    // a buffered output stream to speed things up a bit.
    OutputStream os = contentEncoding != null ? new GZIPOutputStream(out, BUFFER_SIZE) : out;

    // Always send a JSON object.
    os.write('{');

    // Keys used by the HashMaps.
    Set<String> keys = jsonParams.keySet();

    int keysCount = keys.size();
    if (0 < keysCount) {
      int keysProcessed = 0;
      boolean isFileWrapper;

      // Go over all keys and handle each's value.
      for (String key : keys) {
        // Indicate that this key has been processed.
        keysProcessed++;

        try {
          // Evaluate the value (which cannot be null).
          Object value = jsonParams.get(key);

          // Write the JSON object's key.
          os.write(escape(key));
          os.write(':');

          // Bail out prematurely if value's null.
          if (value == null) {
            os.write(JSON_NULL);
          } else {
            // Check if this is a FileWrapper.
            isFileWrapper = value instanceof RequestParams.FileWrapper;

            // If a file should be uploaded.
            if (isFileWrapper || value instanceof RequestParams.StreamWrapper) {
              // All uploads are sent as an object containing the file's details.
              os.write('{');

              // Determine how to handle this entry.
              if (isFileWrapper) {
                writeToFromFile(os, (RequestParams.FileWrapper) value);
              } else {
                writeToFromStream(os, (RequestParams.StreamWrapper) value);
              }

              // End the file's object and prepare for next one.
              os.write('}');
            } else if (value instanceof JsonValueInterface) {
              os.write(((JsonValueInterface) value).getEscapedJsonValue());
            } else if (value instanceof org.json.JSONObject) {
              os.write(value.toString().getBytes());
            } else if (value instanceof org.json.JSONArray) {
              os.write(value.toString().getBytes());
            } else if (value instanceof Boolean) {
              os.write((Boolean) value ? JSON_TRUE : JSON_FALSE);
            } else if (value instanceof Long) {
              os.write((((Number) value).longValue() + "").getBytes());
            } else if (value instanceof Double) {
              os.write((((Number) value).doubleValue() + "").getBytes());
            } else if (value instanceof Float) {
              os.write((((Number) value).floatValue() + "").getBytes());
            } else if (value instanceof Integer) {
              os.write((((Number) value).intValue() + "").getBytes());
            } else {
              os.write(escape(value.toString()));
            }
          }
        } finally {
          // Separate each K:V with a comma, except the last one.
          if (elapsedField != null || keysProcessed < keysCount) {
            os.write(',');
          }
        }
      }

      // Calculate how many milliseconds it took to upload the contents.
      long elapsedTime = System.currentTimeMillis() - now;

      // Include the elapsed time taken to upload everything.
      // This might be useful for somebody, but it serves us well since
      // there will almost always be a ',' as the last sent character.
      if (elapsedField != null) {
        os.write(elapsedField);
        os.write(':');
        os.write((elapsedTime + "").getBytes());
      }

      AsyncHttpClient.log.i(
          LOG_TAG, "Uploaded JSON in " + Math.floor(elapsedTime / 1000) + " seconds");
    }

    // Close the JSON object.
    os.write('}');

    // Flush the contents up the stream.
    os.flush();
    AsyncHttpClient.silentCloseOutputStream(os);
  }