前回の記事で Google スプレッドシートの内容を csv に変換して保存しました。
【GAS】SpreadSheet を csv にして Drive に保存する
なので、今回はその csv データを LINEWORKS のコンテンツアップロード API を使ってアップロードしたいと思います。
マニアックー♪ (*‘∀‘)
あ、でも別に続きってわけじゃないので、前の記事を見ていなくてもご安心を。
前置きはいいから完成したコードを見たい!って方は読み飛ばしてコチラまでどーぞ。
アップロードする手順
- スプレッドシートの内容を csv データに変換する
- LINEWORKS API の認証トークンを取得する
- 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-Type は multipart/form-data
が指定されています。
複数の種類のデータを一度に扱える形式で、ファイルアップロードではよく利用される形式です。
multipart/form-data
を使用するためには Content-Type
で boundary
を指定し、データは payload
にセットします。
const options = {
"contentType": "multipart/form-data; boundary=" + boundary,
"payload": payload
}
では、boundary
と payload
の中身を作っていきましょう。
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 のドキュメントで name
は resourceName
と記載するように指示されているので注意しましょう。
好きな名前じゃだめですよ!('ω')
そうしたら次に 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
を表示するようにしているので、ログを確認します。
成功ですね!ヾ(´∀`)ノ
この resourceId
を使って Bot でファイル送信ができます。
【LINEWORKS API】新機能!BOT でファイルを送受信!
もちろん、コンテンツダウンロード API を使ってダウンロードすることもできます。
ちなみにこちらの記事でも書きましたが、resourceId
は body
ではなく 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から送信する