GyazoとImgurのapiを使ってストレージを節約 with Vue

![スクリーンショット (64).png](https://qiita-image-store.s3.amazonaws.com/0/222473/9d6dabec-8d15-9945-9842-19e767cb1fb2.png)
DEMOページ [https://gyazo-imgur.netlify.com/](https://gyazo-imgur.netlify.com/)
GitHub [https://github.com/ErgoFriend/gyazo-imgur](https://github.com/ErgoFriend/gyazo-imgur)
画像をアップロードしてgyazoもしくはimugrのurlを返してくれます。
書いてる途中にエンコードすればいいのかって思いましたけど、どうなんでしょうかねその辺。

## ドキュメント
### Gyazo
ドキュメント [https://gyazo.com/api?lang=ja](https://gyazo.com/api?lang=ja)
アップロード エンドポイント [https://upload.gyazo.com/api/upload](https://upload.gyazo.com/api/upload)
### Imgur
ドキュメント [https://apidocs.imgur.com/](https://apidocs.imgur.com/)
アプリケーションを作成する [https://api.imgur.com/oauth2/addclient](https://api.imgur.com/oauth2/addclient)
アップロード エンドポイント [https://api.imgur.com/3/image](https://api.imgur.com/3/image)

## JavaScript
### javascriptでapiを利用する
今回はfetchを使用してます。
Gyazoにアップロードする際に、`Authorization = 'Client-ID ' + apiKey;`をヘッダーに含めてしまうとAccess-Control-Allow-Originエラーが出てしまいまいした。
なので、Vueでは条件分岐させています。

```javascript
if (this.api.service == 'gyazo') {
  data.append('imagedata', files[0]);
  data.append('access_token', apiKey);
} else {
  data.append('image', files[0]);
  content.headers.Authorization = 'Client-ID ' + apiKey;
}
```

```javascript
// https://api.imgur.com/3/image
// https://upload.gyazo.com/api/upload
const apiUrl = '';
// https://imgur.com/account/settings/apps
// https://gyazo.com/oauth/applications
const apiKey = '';

// Common
let data = new FormData();
let content = {
  method: 'POST',
  headers: {
    Accept: 'application/json'
  },
  body: data,
  mimeType: 'multipart/form-data'
};

// Gyazo
data.append('imagedata', files[0]);
data.append('access_token', apiKey);
// Imgur
data.append('image', files[0]);
content.headers.Authorization = 'Client-ID ' + apiKey; // Don't include gyazo api

fetch(apiUrl, content)
  .then(
    response => response.json() // if the response is a JSON object
  )
  .then(success => {
    console.log(success);
    console.log(success.url); // Gyazo API
    console.log(success.data.link); // Imgur API
  })
  .catch(
    error => console.log(error) // Handle the error response object
  );
```
## Vue
### 環境変数
GyazoとImgurのAPIトークンは`token: process.env.VUE_APP_GYAZO`のように環境変数から読み込んでいます。
環境変数を使うには、プロジェクトルートに`.env.local`というファイルを作成します。(`package.json`があるとこ)
変数名の最初に`VUE_APP_`を付けることでvueから使用できます。

```.env:.env.local
VUE_APP_GYAZO='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
VUE_APP_IMGUR='zzzzzzzzzzzz'
```
### コンポーネント
`Uploader.vue`の`props`に`api`というオブジェクト変数を作ります。
親コンポーネントから渡した例

```vue:App.vue
<template>
  <div id="app">
    <Uploader class="api" :api="gyazo"/>
    <Uploader class="api" :api="imgur"/>
  </div>
</template>
<script>
import Uploader from './components/Uploader.vue';
export default {
  name: 'app',
  components: {
    Uploader
  },
  data() {
    return {
      imgur: {
        service: 'imgur',
        url: 'https://api.imgur.com/3/image',
        token: process.env.VUE_APP_IMGUR,
        logo: 'data:image/jpeg;base64,xxxxxxxxxxxxxxxxxxxxx'
      },
      gyazo: {
        service: 'gyazo',
        url: 'https://upload.gyazo.com/api/upload',
        token: process.env.VUE_APP_GYAZO,
        logo: 'https://i.imgur.com/ylzMVIk.png'
      }
    };
  }
};
</script>
```

```vue:Uploader.vue
<template>
  <div class="api_container">
    <div style="height: 160px;">
      <img :src="api.logo" style="max-width: 300px;"/>
    </div>
    <br>
    <input v-show="!image_url" type="file" id="image_file" @change="onFileChange"  class="imgur" accept="image/*" required/>
    <button v-show="image_url" type="button" @click="removeImage">取り消す</button>
    <br>
    <p>URL: {{ image_url }}</p>
    <br>
    <div style="height: 300px;">
      <img :src="image_thum" id="image_thum" style="max-width: 300px;"/>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    api: {
      type: Object,
      logo: String,
      url: String,
      token: String,
      service: String
    }
  },
  data() {
    return {
      loading_gif:
        'https://cdn-images-1.medium.com/max/1600/1*9EBHIOzhE1XfMYoKz1JcsQ.gif',
      image_thum_sample:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTaE2sVZUD0TFLL7ix68tjo2PG0cgxb_NFR65Thfr_VuC45yGfT',
      image_thum:
        'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTaE2sVZUD0TFLL7ix68tjo2PG0cgxb_NFR65Thfr_VuC45yGfT',
      image_url: null
    };
  },
  methods: {
    onFileChange: function(e) {
      let files = e.target.files || e.dataTransfer.files;
      if (!files.length) {
        return;
      }

      const apiUrl = this.api.url;
      const apiKey = this.api.token;

      this.image_thum = this.loading_gif;
      this.image_url = ' アップロードしています...';

      let data = new FormData();
      let content = {
        method: 'POST',
        headers: {
          //Authorization: 'Client-ID ' + apiKey,
          Accept: 'application/json'
        },
        body: data,
        mimeType: 'multipart/form-data'
      };

      if (this.api.service == 'gyazo') {
        console.debug('gyazo');
        data.append('imagedata', files[0]);
        data.append('access_token', apiKey);
      } else {
        console.debug('imgur');
        data.append('image', files[0]);
        content.headers.Authorization = 'Client-ID ' + apiKey;
      }

      fetch(apiUrl, content)
        .then(
          response => response.json() // if the response is a JSON object
        )
        .then(
          success => {
            console.log(success);
            if (this.api.service == 'gyazo') {
              console.log(success.url);
              this.image_url = success.url;
            } else {
              console.log(success.data.link);
              this.image_url = success.data.link;
            }
            this.createImage(files[0]);
          } // Handle the success response object
        )
        .catch(
          error => console.log(error) // Handle the error response object
        );
    },
    createImage(file) {
      let reader = new FileReader();
      reader.onload = e => {
        this.image_thum = e.target.result;
      };
      reader.readAsDataURL(file);
    },
    removeImage() {
      this.image_url = '';
      document.getElementById('image_file').value = '';
      this.image_thum = this.image_thum_sample;
    }
  }
};
</script>
```