KintoneからChatworkへのファイル送信を行ないたい

何を実現したいのかを書きましょう

Kintoneのレコード詳細画面にボタンを設置し、
そのレコード内の
ひとつのテキスト項目と、
添付ファイルをChatworkに送信したいという要望があります。
テキストの送信は出来たのですが、
ファイルの送信が、うまくいきません。
おそらく

ファイルをダウンロードする

外部にファイルをアップロードする

をという形で実現できるのでは?と考えているのですが、
ダウンロードの段階で、データが取得できません。

発生した問題やエラーメッセージを具体的に書きましょう

まず、ダウンロードが出来ているのか確認したいと思い、
下記コードで試したのですが、
アラートで出てくるレスポンスが空のようで {} と表示されます。
非同期処理だからかもしれないと思い、
対応したコードにしたつもりです。
また、アラートは、
14行目と17行目のURLと、
27行目のJSON.stringifyしたものは出るのですが、
36行目、38行目のアラートは表示されません。
おそらく、そこまで処理が進まずに止まってしまうんだと思うのですが、
ご教示いただければ幸いです。
よろしくお願いいたします。

実行したコードをコピー&ペーストしましょう

コードをここに入力または貼り付け

kintone.events.on([‘app.record.create.show’, ‘app.record.edit.show’,‘app.record.detail.show’], event => {
const sp2 = kintone.app.record.getSpaceElement(‘button_space_1’);
const btn2 = document.createElement(‘button’);
btn2.textContent=‘ボタン2’;
sp2.appendChild(btn2);
btn2.onclick=()=>{
var subdomain = “xxxxx”;
var cw_token = ‘xxxxxxxxxxxxxxxx’;
var room_id = ‘xxxxxxxxx’;
var self_unread = 1;

  var filekey=event.record['添付ファイル_0'].value[0].fileKey;
  var url2=kintone.api.urlForGet('/k/v1/file.json', {fileKey: filekey}, true);
alert(url2);
// ファイルダウンロードAPI を実行します。
const ret = (url) => {
  alert(url);
  return new Promise((resolve,reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url,true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.responseType = 'blob';
    xhr.onload = () => {
      if (xhr.status === 200) {
        // ファイルがレスポンスとして返却されます。
        console.log(xhr.response);
        alert(JSON.stringify(xhr.response));
      }
    };
    xhr.send();
  });   
};

ret(url2).then((res) => {
  const blob = new Blob(res);
  alert(blob);      
}).catch((err) =>{
  alert(err);
});

}

return event;
});

上記、修正してダウンロードの方は成功していることが確認できましたが、
送信部分がエラーになってしまいます。

コミュニティをしらべて
kintone.proxyも、kintone.proxy.uploadもダメらしいことがわかったので、
XMLHttpRequestを使えば行けそう・・・と思うのですが、
POSTのonerrorになってしまうばかりで、
エラーメッセージも返っておらず、対応に苦慮しております。

みなさまのお知恵をお貸しいただければと思います
よろしくお願いいたします

kintone.events.on(['app.record.create.show', 'app.record.edit.show','app.record.detail.show'], event => {
  const sp2 = kintone.app.record.getSpaceElement('button_space_1');
  const btn2 = document.createElement('button');
  btn2.textContent='ボタン2';
  sp2.appendChild(btn2);
  btn2.onclick=()=>{
	  var subdomain = "xxxxx";
	  var kt_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
	  var cw_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
	  var room_id = 'xxxxxxx';
	  var filekey=event.record['添付ファイル_0'].value[0].fileKey;
	  var url2=kintone.api.urlForGet('/k/v1/file.json', {fileKey: filekey}, true);

    //ファイルのダウンロード
    const ret = (url) => {
      return new Promise((resolve,reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url,true);
        xhr.setRequestHeader("X-Cybozu-API-Token", kt_token);
        xhr.onload = () => {
          if (xhr.status === 200) {
            console.log(xhr.response);
            resolve(xhr.response);
          }
        };
        xhr.onerror=function(){
          reject("ERROR"+xhr.statusText);
        };
        xhr.send();
      });   
    };

    //CWに送信
    const pos = (url3,formData3) => {
      return new Promise((resolve2,reject2) => {
        const xhr2 = new XMLHttpRequest();

        xhr2.open('POST', url3,true);
        xhr2.setRequestHeader('X-ChatWorkToken', cw_token);
//        xhr2.setRequestHeader('Content-Type', `multipart/form-data`);
        xhr2.setRequestHeader('Content-Type', `application/json`);

        xhr2.onload = function(){
            if (xhr2.status === 200) {
              resolve2(xhr2.response);
            }else{
              resolve2("ERR"+xhr2.statusText);
            }
        };
        xhr2.onerror=function(){
          reject2("ERROR_POS"+xhr2.statusText);
        };
        xhr2.send(formData3);
      });   
    };

    ret(url2).then((res) => {
      var blob =  new Blob([res]);

	    var url = 'https://api.chatwork.com/v2/rooms/'+ room_id +'/files';
		  var headers = {
			    	'X-ChatWorkToken': cw_token,
		  };
		  var formData = new FormData();
		  formData.append('file',[res]);
      pos(url,formData).then((res2) => {
        alert("Suc?"+res2);
      }).catch((res2) => {
        alert("ERR"+JSON.stringify(res2));
      });      

    }).catch((res) => {
      alert(res);
    });

  }
  return event;
});    

不明な点が少しずつ変わってきたので、再度質問です。
現状、ファイルのダウンロード部分は出来ていると思います。
問題はChatworkへの送信部分で、
下記コードを実行すると、
POST処理の onerror イベントに設定したアラートが表示され、
内容は 「 ERR{} 」となり、エラーの内容は表示されません。
ということは、XhrPOST.send(formData) が実行されていないのではないかと思うのですが、
何が原因でsendしてくれないのか、判別がつきません。
この原因がなんなのか、あるいは、この原因を判別する方法があれば、
教えてください。
よろしくお願いいたします。

kintone.events.on(['app.record.create.show', 'app.record.edit.show','app.record.detail.show'], event => {
  const sp2 = kintone.app.record.getSpaceElement('button_space_1');
  const btn2 = document.createElement('button');
  btn2.textContent='ボタン2';
    //スペースフィールドにボタンを追加する
  sp2.appendChild(btn2);

    //ボタンをクリックしたときの動作
  btn2.onclick=()=>{
	  var subdomain = "xxxxxxx";
	  var kt_token = "xxxxxxxxxxxxxxxxxxxxxxxxx"; //このアプリのAPIトークン
	  var cw_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'; //CWのトークン
	  var room_id = 'xxxxxxx'; //CWのroomId 

		//初期設定ここまで

	  var filekey=event.record['添付ファイル_0'].value[0].fileKey;
	  var filename=event.record['添付ファイル_0'].value[0].name;
	  alert(filename);
	  var url=kintone.api.urlForGet('/k/v1/file.json', {fileKey: filekey}, true);

      const xhr = new XMLHttpRequest();
      xhr.open('GET', url,true);
      xhr.setRequestHeader("X-Cybozu-API-Token", kt_token);
      xhr.onreadystatechange = function(){
        if ((xhr.readyState == 4) && (xhr.status == 200)){
          // ファイルがレスポンスとして返却されます。
          alert("xhr.response="+xhr.response); //ファイルのデータは閲覧できます
          var urlPOST = 'https://api.chatwork.com/v2/rooms/'+ room_id +'/files';
          var formData = new FormData();
          var blob= new Blob([xhr.response]);
          formData.append('file',blob,filename);

          var xhrPOST = new XMLHttpRequest();
          xhrPOST.open('POST',urlPOST,true);
          xhrPOST.setRequestHeader('Accept', 'application/json');
          xhrPOST.setRequestHeader('X-ChatWorkToken', cw_token);
          xhrPOST.onload = function(){
            alert("POST="+xhrPOST.status+"/"+xhrPOST.response);
            if ((xhrPOST.status == 200)){
              alert("POSTSUCCESS"+xhrPOST.response);
            }else{
              alert("POSTERR"+JSON.stringify(xhrPOST.response));
            }
          };
	  	    xhrPOST.onerror=function(){
		        alert("ERR"+JSON.stringify(xhrPOST));   //毎回、この部分のアラートが表示される
  		    };
	  	    xhrPOST.send(formData);
        }
      };
      xhr.onerror=function(){
        alert("ERROR"+xhr.responseText);
      };
      xhr.send();

  };
  return event;
});    

ブラウザのデベロッパーツールで確認したところ、
下記のエラーが発生していました。

Access to XMLHttpRequest at 'https://api.chatwork.com/v2/rooms/{room_ID}/files' from origin 'https://{サブドメイン}.cybozu.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Google翻訳すると

『オリジン「https://{サブドメイン}.cybozu.com」から「https://api.chatwork.com/v2/rooms/{room_ID}/files」の XMLHttpRequest へのアクセスは CORS ポリシーによってブロックされています: プリフライト リクエストへの応答は行われませんアクセス制御チェックに合格しました: 要求されたリソースに「Access-Control-Allow-Origin」ヘッダーが存在しません。』

となり、要はChatwork側の返信に「Access-Control-Allow-Origin」ヘッダーが無いためエラーになっているようなので、
調べて

 xhrPOST.setRequestHeader('Origin', 'https://'+subdomain+'.cybozu.com');
xhrPOST.withCredentials = true;

を加えてsendしてみましたが、結果は変わりません。
ということは、chatworkへのファイル送信は不可能?ということなんでしょうか?
なにか対策無いでしょうか?

「いいね!」 1

こちら、時間が経ちましたが解決はされましたでしょうか?
たまたま回答されてないことに気づきましたので、僭越ながらコメントさせてもらいますと、

  • Chartworkのファイル送信APIは multipart/form-data 形式で受け付けてそう
  • kintone.proxy.upload() はその形式でuploadできない(のでご認識の通り
  • Chatwork(に限らず多くのAPIで)は、セキュリティ対策の為CORSによる制約があり、ブラウザから直接利用できない、ので、xhrで処理できない

という前提があります。

ただし、ファイル送信は不可能か、という問に対してはNoで、具体的には、
サーバー間通信をすればCORSを回避できます、ので
kintone→自前のサーバー→ChatworkAPI とすればいけます。
自前のサーバーにkintoneから受けれる口をつくるイメージですね。自前なのでCORSの設定も好きに変更できるイメージです。
例えばAWSのAPI Gatewayを利用する方法もありますね。

もしくは、バッチ処理で、自前のサーバーからkintone REST API経由でデータを取得し、Chatworkに送る方法も取れると思います。