tw-minus 画像ツイート (bug 403 fix)

UserJS な自作 Twitter クライアント tw-minus で、画像投稿ができなくなっていた。原因は、Twitter API の細かい仕様変更だった。

主に 2 つの原因があった。

1 つ目は、POST する際のクエリの名前が間違っていたというもの。

画像データを乗せる部分の名前 media_data[] が廃止されていたために、403 エラーとなっていた。新しい名前は media[] である。

2 つ目は、画像データの形式。

今までは、画像のバイナリを base64エンコードして送信していたが、新しい仕様では、base64 の場合、Content-Transfer-Encoding: base64 という文字列をヘッダに含めなければならなくなった。

なので、xhr.setRequestHeader("Content-Transfer-Encoding", "base64"); を試したが、通らなかった。

なんでかというと、セットする場所がそこではないから。

multipart/form-data では、色々な形式のデータをまとめて送信することができる。たとえば、PNG 画像と、ZIP ファイル、テキストデータなどが混在できる。それらは、一つ一つのパートであり、一つのパートには、一つ以上のヘッダを設定できる。

すなわち、画像データを含めるパートである media[] のヘッダに Content-Transfer-Encoding: base64 を付加しなければならない。

が、HTML5 の FormData には、パートごとにヘッダを手動で指定する方法が用意されていない。

そのため、パートにヘッダを指定するやり方をもって画像投稿をするのは不可能っぽいわけで。

それならば、base64 でなく、生バイナリで送れないものかと色々と調べたり試したりしたところ、可能だった。

以下の HTML があるとする。

<input type="file" id="file" />

このコントロールで、画像ファイルを 1 つ選択する。

以下の JS で、その画像ファイルの File オブジェクトを得ることができる。

var file = document.querySelector("#file").files[0];

こうして得た File オブジェクトは、FormData オブジェクトの append メソッドの第二引数に渡すことができる。

var fd = new FormData;
fd.append("media[]", file);

こうして File オブジェクトを append した FormData オブジェクトを xhr.send の第一引数に渡せば、画像のバイナリデータを送ることができる。

var xhr = new XMLHttpRequest;
xhr.send(fd);

以上。このやり方で、tw-minus で画像ツイートできるようになった。

今回の修正における差分は、以下で参照できる。