2018-03-19

multipart/form-dataのファイルをGoogle Apps Scriptから送信する

Google Apps Scriptでファイルを送れない


HTTPの通信を用いて、ファイルを送るとき、curlやpythonでは、コマンドでfile指定すれば、簡単にmultipartのファイルを送れる。しかし、Google Apps Script上では、簡単にmultipartを送信することができない。




ファイルを送信するときは、multipart/form-dataを利用しているらしい


今、Microsoft Face APIを利用して、画像のバイナリデータを送信したいのだが、fileメソッドがないので、自作する必要がある。PythonからFace APIで画像のバイナリファイルを送信することはできるようで、その時はrequests.post(file = hoge)で成功していた。なのえd、ということで、Pythonのrequestsモジュールのfileではどのような処理をしているのか調べたところ、multipartという言葉に出会った



ちなみにファイルを送信するときに一般的に利用されるのは、multipart/form-dataもあるが、base64方式で送信することもあるそう。ただし、ほとんどの場合は前者を利用するとが多いみたいだ。


multipart/form-dataとは


HTTP Multipartリクエストは、HTTPサーバーにファイルやそれ以外のファイルを送信す
るときに利用される。multipart/form-dataメッセージは、boundariesによって区切られたいくつかの部分がある。それぞれの部分は、Content-Diposisionヘッダーがspン材していて、その値はform-dataとなっている。もし、そのファイルがサーバーに送られるときは、ContentTypeも一緒に含まれている必要がある。

The HTTP multipart request is commonly used to upload files and other data over to a HTTP Server. A “multipart/form-data” message contains a series of parts separated by boundaries. Each part should contain the “Content-Disposition” header whose value is “form-data” and if a file is being sent to the server, the contentType should also be included.(Upload Files using the Multipart post method with Google Apps Scriptから翻訳

らしい。これだけ読んでもよくわからない。


multipart/form-dataについて調べてみる

multipart/form-dataについての通信規約については、Returning Values from Forms: multipart/form-data(RFC)を参照すると正式な内容がわかるはず。


日本語で翻訳されていてわかりやすいのは、この記事っぽい。

multipart/form-data (MIME)

MIME型 multipart/form-data は、 HTML のフォームの提出で使われるデータ形式です。
application/x-www-form-urlencoded と並んで HTTP POST 要求で最もよく用いられる MIME型の1つです。 パーセント符号化を使う application/x-www-form-url-encoded では扱いづらいファイル (<input type=file>) の提出のために開発されましたが、 ファイルを扱わない場合にも使われています。
らしい。


Google Apps Script経由で、送信するときのサンプルスクリプトは
https://ctrlq.org/code/20096-upload-files-multipart-post
がよいみたいだ。

// Written by Amit Agarwal www.labnol.org

function uploadFile() {
  
  var boundary = "labnol";
  var blob = DriveApp.getFileById(GOOGLE_DRIVE_FILE_ID).getBlob();
  
  var attributes = "{\"name\":\"abc.pdf\", \"parent\":{\"id\":\"FOLDER_ID\"}}";
  
  var requestBody = Utilities.newBlob(
    "--"+boundary+"\r\n"
    + "Content-Disposition: form-data; name=\"attributes\"\r\n\r\n"
    + attributes+"\r\n"+"--"+boundary+"\r\n"
    + "Content-Disposition: form-data; name=\"file\"; filename=\""+blob.getName()+"\"\r\n"
  + "Content-Type: " + blob.getContentType()+"\r\n\r\n").getBytes()
  .concat(blob.getBytes())
  .concat(Utilities.newBlob("\r\n--"+boundary+"--\r\n").getBytes());
  
  var options = {
    method: "post",
    contentType: "multipart/form-data; boundary="+boundary,
    payload: requestBody,
    muteHttpExceptions: true,
    headers: {'Authorization': 'Bearer ' + getBoxService_().getAccessToken()}
  };

  var request = UrlFetchApp.fetch("https://upload.box.com/api/2.0/files/content", options);

  Logger.log(request.getContentText());

}

注目の投稿

 PythonのTweepyを利用して、Twitter APIを利用している。 その中で、ハマったポイントをメモしておく。 まず、Searchに関して。 Twitter検索は、クライアントアプリ側では、全期間の検索が可能になっている。 一方で、APIを利用する際は、過去1週間しか...