Upload a simple file (the size is no more than 150MB).

@param {Object} params
@param {string} options.ifMatch - Only update the file if its ETag
matches this string
@param {string} options.path - path of the file
@param {string} options.body - contents of the file to upload
@param {string} options.contentType - mime type of the file

@return {Promise} A promise for an object with the following structure:
statusCode - HTTP status code
revision - revision of the newly-created file, if any

@private
function (params) {
    var url = 'https://content.dropboxapi.com/2/files/upload';
    var args = {
      path: getDropboxPath(params.path),
      mode: {'.tag': 'overwrite'},
      mute: true
    };

    if (params.ifMatch) {
      args.mode = {'.tag': 'update', update: params.ifMatch};
    }

    return this._request('POST', url, {
      body: params.body,
      headers: {
        'Content-Type': 'application/octet-stream',
        'Dropbox-API-Arg': JSON.stringify(args)
      }
    }).then((response) => {
      if (response.status !== 200 && response.status !== 409) {
        return Promise.resolve({statusCode: response.status});
      }

      var body = response.responseText;

      try {
        body = JSON.parse(body);
      } catch (e) {
        return Promise.reject(new Error('Invalid API result: ' + body));
      }

      if (response.status === 409) {
        if (compareApiError(body, ['path', 'conflict'])) {
          return this._getMetadata(params.path).then(function (metadata) {
            return Promise.resolve({
              statusCode: 412,
              revision: metadata.rev
            });
          });
        }
        return Promise.reject(new Error('API error: ' + body.error_summary));
      }

      this._revCache.set(params.path, body.rev);

      return Promise.resolve({ statusCode: response.status, revision: body.rev });
    });
  }