3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【GAS × LINEWORKS】CSV を LINEWORKS にアップロードする

Last updated at Posted at 2020-10-20

前回の記事で Google スプレッドシートの内容を csv に変換して保存しました。
【GAS】SpreadSheet を csv にして Drive に保存する

なので、今回はその csv データを LINEWORKS のコンテンツアップロード API を使ってアップロードしたいと思います。

マニアックー♪ (*‘∀‘)

あ、でも別に続きってわけじゃないので、前の記事を見ていなくてもご安心を。

前置きはいいから完成したコードを見たい!って方は読み飛ばしてコチラまでどーぞ。

アップロードする手順

  1. スプレッドシートの内容を csv データに変換する
  2. LINEWORKS API の認証トークンを取得する
  3. csv データを LINEWORKS サーバにアップロードする

おおまかに分けてこんな感じですね~。
では、さっそくやっていきましょう!

スプレッドシートの内容を csv データに変換する

スプレッドシート内のデータを読み込んで、csv 形式の blob データを作成します。

blob は GAS 独自のオブジェクトで、ファイル名と MIME タイプの指定がついています。
Google Drive などの Google API を利用してファイルをやりとりするときによく使います。
JavaScript の Blob とは似ていますが違うものなので注意です。(`・ω・´)

ではでは、その blob データを作成するコードです。

function uploadLw(){
  const csvData = loadData();
  const blob = createBlob(csvData);
}

function loadData() {
  const data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
  const csv = data.map((value) => { return value.join() + "\r\n" }).join('');
  return csv
}

function createBlob(csv) {
  const fileName = 'テスト.csv'
  const contentType = 'text/csv';
  const charset = 'utf-8';
  const blob = Utilities.newBlob('', contentType, fileName).setDataFromString(csv, charset);
  return blob;
}

ちょこっと解説をば。

function loadData() でスプレッドシートから読み込んだデータは二次元配列になっています。
[{"セルA1","セルB1","セルC1"},{"セルA2","セルB1","セルC1"},{....}]

なので、まずは一列ずつカンマ区切りされたテキストにします。

data.map((value) => { return value.join() + "\r\n" })

.map で要素を呼び出して .join で結合しています。最後に改行コードですね。
この時点でのデータの中身を見るとこんな感じです。
{"セルA1,セルB1,セルC1\r\n","セルA2,セルB1,セルC1\r\n","...."}

これをさらに .join させます。
一つだけ注意ですが、.join は引数を何も入れないと自動的にカンマ , で区切りますので、行を結合させるときには区切り文字を使わないよう .join("") と空文字を指定します。

データの中身を見てみると、String データになっています。
"セルA1,セルB1,セルC1\r\nセルA2,セルB1,セルC1\r\n...."

あとはこの String データを Utilities.newBlob().setDataFromString() で blob データに変換すれば OK!
csv 形式の blob データの完成です!

LINEWORKS API の認証トークンを取得する

こんなマニアックな記事を読んでいる方々には釈迦に説法かと思いますので、解説は省略させていただきます。
いちおう、私が作ったライブラリを使うと簡単にトークン発行できますよー、と宣伝をしておきますw
LINEWORKS ライブラリを登録する

コードはこんな感じです。

  const obj = {
    "apiId" : "API ID",
    "consumerKey" : "Server API Consumer Key",
    "serverId" : "Server List(ID登録タイプ) の ID",
    "privateKey" : "Server List(ID登録タイプ) の認証キー",
    "botNo" : "botNo"
  };
  const token = LINEWORKS.getToken(obj);

csv データを LINEWORKS サーバにアップロードする

いよいよ今回、一番苦戦したところ。
LINEWORKS のコンテンツアップロード API を使ってアップロードしたいと思います。

コンテンツアップロード API の Content-Type

コンテンツアップロード API の Content-Typemultipart/form-data が指定されています。
複数の種類のデータを一度に扱える形式で、ファイルアップロードではよく利用される形式です。

multipart/form-data を使用するためには Content-Typeboundary を指定し、データは payload にセットします。

  const options = {
    "contentType": "multipart/form-data; boundary=" + boundary,
    "payload": payload
  }

では、boundarypayload の中身を作っていきましょう。

boundary

boundary は区切り文字なので、中身はなんでも良いです。(`・ω・´)
まぁ、誰の何のデータかわかりやすい文字列にする、くらいですかね。
ex) WebKitFormBoundaryxxxxyz

詳しく知りたい方はこちらの記事をご参考ください。
【HTTP】multipart/form-data の boundary って何ぞや?

payload

こっちはちょっと複雑です。
実際に送るデータの中身を記述するのですが、色々とお作法があります。

まずは、payload を作成するコードを見てみましょう。

function uploadLw(){
  const csv = loadData();
  const blob = createBlob(csv);
  
  const boundary = "WebKitFormBoundaryxxxxyz";
  const headersText =  "--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"resourceName\"; filename=\"" + blob.getName() + "\"\r\n"
    + "Content-Type: " + blob.getContentType() + "\r\n\r\n";
  const footersText = "--" + boundary + "--\r\n";

  const payloadHeaders = Utilities.newBlob(headersText).getBytes();
  const payloadBody = blob.getBytes();
  const payloadFooters = Utilities.newBlob(footersText).getBytes();
  const payload = payloadHeaders.concat(payloadBody).concat(payloadFooters);
}

payload は見ていただいた通り「ここからがデータだよ!」という宣言をしている Header 部分、実データ部分、「ここで終わりだよ!」という宣言をしている Footer 部分に分かれています。

まずは、Header 部分のテキストデータから作成していきます。

payloadHeadersText

お作法として -- から始まり、boundary を書いたら改行します。
次の行には Content-Disposition: form-data; と記載して、これは form-data だヨって明示します。

次に name を記載するのですが、LINEWORKS のドキュメントで nameresourceName と記載するように指示されているので注意しましょう。
好きな名前じゃだめですよ!('ω')

そうしたら次に filename を記載します。
先ほど作成した blob ファイルに filename は設定していますので blob.getName() で取得して設定して、改行します。

最後に送信するデータの Content-Type を記載します。
これも先ほど作成した blob ファイルに設定してありますので blob.getContentType() で取得して設定します。

  const headersText =  "--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"resourceName\"; filename=\"" + blob.getName() + "\"\r\n"
    + "Content-Type: " + blob.getContentType() + "\r\n\r\n";

payloadFootersText

同じく、お作法として -- から始まり、boundary を書いたら -- で閉じて、最後に改行します。これは簡単ですね。

  const footersText = "--" + boundary + "--\r\n";

blob データを作成してデータストアを取得する

テキストデータができたら blob 形式に変換し、.getBytes() を使用してデータストアを取得します。

  const payloadHeaders = Utilities.newBlob(headersText).getBytes();
  const payloadBody = blob.getBytes();
  const payloadFooters = Utilities.newBlob(footersText).getBytes();

最後に .concat() で結合して完成です!

  const payload = payloadHeaders.concat(payloadBody).concat(payloadFooters);

コンテンツアップロード API を Request する

データが揃ったので、いよいよ API を Request します!

  const headers = {
    "consumerKey": obj.consumerKey,
    "Authorization": "Bearer " + LINEWORKS.getToken(obj),
    "x-works-apiid": obj.apiId
  }
  
  const options = {
    "method": "post",
    "headers": headers,
    "contentType": "multipart/form-data; boundary=" + boundary,
    "payload": payload,
    "muteHttpExceptions": true
  };

  const uri = "http://storage.worksmobile.com/openapi/message/upload.api";
  const response = UrlFetchApp.fetch(uri, options);
  const resHeaders = response.getHeaders();
  Logger.log("x-works-resource-id: " + resHeaders["x-works-resource-id"]);

ログにアップロードしたファイルの resourceId を表示するようにしているので、ログを確認します。

1603098671.png

成功ですね!ヾ(´∀`)ノ
この resourceId を使って Bot でファイル送信ができます。
【LINEWORKS API】新機能!BOT でファイルを送受信!

もちろん、コンテンツダウンロード API を使ってダウンロードすることもできます。

ちなみにこちらの記事でも書きましたが、resourceIdbody ではなく headers の中に入っているのでご注意くださいね~(; ・`д・´)
LINEWORKS のコンテンツアップロード API を使う

コード全体

最後にコード全体をさらしておきます。

const obj = {
  "apiId" : "API ID",
  "consumerKey" : "Server API Consumer Key",
  "serverId" : "Server List(ID登録タイプ) の ID",
  "privateKey" : "Server List(ID登録タイプ) の認証キー",
  "botNo" : "botNo"
};

function uploadLw(){
  const csv = loadData();
  const blob = createBlob(csv);
  
  const boundary = "WebKitFormBoundaryxxxxyz";
  const headersText =  "--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"resourceName\"; filename=\"" + blob.getName() + "\"\r\n"
    + "Content-Type: " + blob.getContentType() + "\r\n\r\n";
  const footersText = "--" + boundary + "--\r\n";

  const payloadHeaders = Utilities.newBlob(headersText).getBytes();
  const payloadBody = blob.getBytes();
  const payloadFooters = Utilities.newBlob(footersText).getBytes();
  const payload = payloadHeaders.concat(payloadBody).concat(payloadFooters);

  const headers = {
    "consumerKey": obj.consumerKey,
    "Authorization": "Bearer " + LINEWORKS.getToken(obj),
    "x-works-apiid": obj.apiId
  }
  
  const options = {
    "method": "post",
    "headers": headers,
    "contentType": "multipart/form-data; boundary=" + boundary,
    "payload": payload,
    "muteHttpExceptions": true
  };

  const uri = "http://storage.worksmobile.com/openapi/message/upload.api";
  const response = UrlFetchApp.fetch(uri, options);
  const resHeaders = response.getHeaders();
  Logger.log("x-works-resource-id: " + resHeaders["x-works-resource-id"]);
}

function loadData() {
  const data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
  return data.map((value) => { return value.join() + "\r\n" }).join('');
}

function createBlob(csv) {
  const fileName = 'テスト.csv'
  const contentType = 'text/csv';
  const charset = 'utf-8';
  const blob = Utilities.newBlob('', contentType, fileName).setDataFromString(csv, charset);
  return blob;
}

おわりに

ここまでお付き合いいただきありがとうございました。

ようやく GAS でアップロード API 使えた!
GoogleAppsScript で multipart/form-data 形式の記述に関する知見が物凄く少ないんですよ!
やる人、少ないのかな?
それとも、書くまでもないことなのかな??( ゚Д゚)

私がわからなくて検索したとき困ったので、この記事が誰かの役に立てれば幸いです。

ではまた!(^^)/

参考にさせていただきましたm(_ _)m

MDN web docs - Array.prototype.join()
Review of My Life - multipart/form-dataのファイルをGoogle Apps Scriptから送信する

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?