FormDataを力づくで実装

Android4.xとかではHTML5なFormDataは使えない。
バイナリデータを送信したい時どうしようかと探していたけど、一番安定したのがこの方法。

FormDataのMultipartを力づくで実装。

ココを参考に
http://qiita.com/KNaito/items/54b1bf61a3c678ca28b1


ES6でこう書いた。

```js
import Promise from 'es6-promise'

export default class Multipart{
  constructor({url}){
    super()
    this.url = url
    this.init()
  }

  init(){
    this.boundary = this.createBoundary()
    this.buffer = new Uint8Array(0)
  }

  send(){
    return new Promise((resolve, reject)=>{
      var xhr = new XMLHttpRequest();
      xhr.open("POST",this.url, true);
      xhr.setRequestHeader("Content-type" , this.createHeader())
      this.buffer = this.appendBuffer(this.buffer, this.createFooter())
      xhr.send(this.buffer)

      xhr.onreadystatechange = ()=>{
        console.log(xhr.readyState)
        if(xhr.readyState === 4 && xhr.status === 200){
          if(xhr.status === 200){
            const res = JSON.parse(xhr.response)
            resolve(res)
          }else{
            reject(new Error(xhr.status))
          }
        }
      }
    })
  }

  createHeader(){
    if(!this.boundary) return false
    return `multipart/form-data; boundary=${this.boundary}`
  }

  createFooter(){
    return this.unicode2buffer(`\r\n--${this.boundary}--\r\n`)
  }

  append(_key, _value){
    let buffer = this.unicode2buffer(`--${this.boundary}\r\nContent-Disposition: form-data; name="${_key}";`)
    if(typeof _value === 'string'){
      buffer = this.appendBuffer(buffer, this.unicode2buffer(`\r\n\r\n${_value}\r\n`) )
    }

    if(_value instanceof Uint8Array){
      buffer = this.appendBuffer(buffer, this.unicode2buffer(` filename="blob"\r\nContent-Type: image/png\r\n\r\n`) )
      buffer = this.appendBuffer(buffer, _value)
      buffer = this.appendBuffer(buffer, this.unicode2buffer(`\r\n`) )
    }
    this.buffer = this.appendBuffer(this.buffer, buffer)
  }

  /*
   * ランダムなBoundaryStringを生成 Requestごとに一意である
   */
  createBoundary(){
    const multipartChars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const length = 16
    let boundary = "------WebKitFormBoundary";
    for (let i=0; i<length; i++) {
      boundary += multipartChars.charAt( Math.floor( Math.random() * multipartChars.length ) );
    }
    return boundary;
  }

  unicode2buffer(str){

    var n = str.length,
      idx = -1,
      byteLength = 512,
      bytes = new Uint8Array(byteLength),
      i, c, _bytes;

    for(i = 0; i < n; ++i){
      c = str.charCodeAt(i);
      if(c <= 0x7F){
        bytes[++idx] = c;
      } else if(c <= 0x7FF){
        bytes[++idx] = 0xC0 | (c >>> 6);
        bytes[++idx] = 0x80 | (c & 0x3F);
      } else if(c <= 0xFFFF){
        bytes[++idx] = 0xE0 | (c >>> 12);
        bytes[++idx] = 0x80 | ((c >>> 6) & 0x3F);
        bytes[++idx] = 0x80 | (c & 0x3F);
      } else {
        bytes[++idx] = 0xF0 | (c >>> 18);
        bytes[++idx] = 0x80 | ((c >>> 12) & 0x3F);
        bytes[++idx] = 0x80 | ((c >>> 6) & 0x3F);
        bytes[++idx] = 0x80 | (c & 0x3F);
      }
      if(byteLength - idx <= 4){
        _bytes = bytes;
        byteLength *= 2;
        bytes = new Uint8Array(byteLength);
        bytes.set(_bytes);
      }
    }
    idx++;

    var result = new Uint8Array(idx);
    result.set(bytes.subarray(0,idx),0);

    return result.buffer;
  }

  appendBuffer(buf1,buf2) {
    var uint8array = new Uint8Array(buf1.byteLength + buf2.byteLength);
    uint8array.set(new Uint8Array(buf1),0);
    uint8array.set(new Uint8Array(buf2),buf1.byteLength);
    return uint8array.buffer;
  }

}

```


RequestBodyの種類によっては動かないパターンもあるので、適宜調整が必要だとおもいます。
BoundaryStringもWebkitって書いてあるし。（なんでもいいのかな？）

あと、ちゃんとしたFormDataのPolyfillにすればよかったなぁ。