private BatchInfo createBatchFromStreamImpl(JobInfo jobInfo, InputStream input, boolean isZip)
      throws AsyncApiException {
    try {
      String endpoint = getRestEndpoint();
      Transport transport = config.createTransport();
      endpoint = endpoint + "job/" + jobInfo.getId() + "/batch";
      String contentType = getContentTypeString(jobInfo.getContentType(), isZip);
      HashMap<String, String> httpHeaders = getHeaders(contentType);
      // TODO do we want to allow the zip content to be gzipped
      boolean allowZipToBeGzipped = false;
      OutputStream out = transport.connect(endpoint, httpHeaders, allowZipToBeGzipped || !isZip);

      FileUtil.copy(input, out);

      InputStream result = transport.getContent();
      if (!transport.isSuccessful()) parseAndThrowException(result);
      return BatchRequest.loadBatchInfo(result);
    } catch (IOException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    } catch (PullParserException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    } catch (ConnectionException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    }
  }
  /**
   * @param jobInfo Parent job for new batch.
   * @param batchContent InputStream containing the xml or csv content of the batch, or null only if
   *     request.txt is contained in attachments map
   * @param attachments Map of attachments where the key is the filename to be used in the zip file
   *     and the value is the InputStream representing that file.
   * @return BatchInfo of uploaded batch.
   */
  public BatchInfo createBatchWithInputStreamAttachments(
      JobInfo jobInfo, InputStream batchContent, Map<String, InputStream> attachments)
      throws AsyncApiException {

    if (batchContent != null && attachments.get("request.txt") != null)
      throw new AsyncApiException(
          "Request content cannot be included as both input stream and attachment",
          AsyncExceptionCode.ClientInputError);
    try {
      String endpoint = getRestEndpoint();
      endpoint = endpoint + "job/" + jobInfo.getId() + "/batch";
      Transport transport = config.createTransport();
      ZipOutputStream zipOut =
          new ZipOutputStream(
              transport.connect(
                  endpoint,
                  getHeaders(getContentTypeString(jobInfo.getContentType(), true)),
                  false));

      try {
        if (batchContent != null) {
          zipOut.putNextEntry(new ZipEntry("request.txt"));
          FileUtil.copy(batchContent, zipOut, false);
        }
        for (Map.Entry<String, InputStream> entry : attachments.entrySet()) {
          zipOut.putNextEntry(new ZipEntry(entry.getKey()));
          FileUtil.copy(entry.getValue(), zipOut, false);
        }
      } finally {
        zipOut.close();
      }

      InputStream result = transport.getContent();
      return BatchRequest.loadBatchInfo(result);
    } catch (IOException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    } catch (PullParserException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    } catch (ConnectionException e) {
      throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e);
    }
  }