Vue2でファイルのアップロードを処理する方法
序章
この記事では、VueJでファイルのアップロードを処理する方法について説明します。 ユーザーがドラッグアンドドロップまたはファイル選択ダイアログで単一または複数の画像ファイルをアップロードできるようにする画像アップローダーを作成します。
次に、選択した画像をアップロードし、それに応じて表示します。 また、アップロードファイルタイプのフィルタリングについても学習します。たとえば、画像のみを許可し、PDFなどのファイルタイプは許可しません。
- ソースコード: https://github.com/chybie/file-upload-vue
- デモ: https://vue-file-upload-1126b.firebaseapp.com/
ファイルアップロードUIとAPI
ファイルのアップロードは、 UI(フロントエンド)と API(バックエンド)の2つの部分で構成されています。 UI部分の処理にはVueJsを使用します。 アップロードされたファイルを受け入れるには、バックエンドアプリケーションが必要です。 バックエンドのチュートリアルに従うか、これらのサーバー側アプリケーションのいずれかをダウンロードして実行し、バックエンドのファイルアップロードを処理できます。-
- Hapi.jsを使用したファイルのアップロード: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js 、または
- Express + Multerを使用したファイルのアップロード: https://scotch.io/tutorials/express-file-uploads-with-multer 、または
- 選択したクラウドソリューション(Amazon S3、Googleドライブなど)に切り替えます。
この記事では、バックエンドとしてファイルのアップロードとHapi.jsを使用します。 また、フロントエンドで偽のアップロードを有効にするための秘訣についても学びます。
Vue-Cliを使用したプロジェクトのセットアップ
vue-cliを使用してVue.jsプロジェクトをスキャフォールディングします。 webpack-simple
プロジェクトテンプレートを使用します。
# install cli
npm install vue-cli -g
# then create project, with sass
# follow the instructions to install all necessary dependencies
vue init webpack-simple file-upload-vue
了解しました。すべて設定しました。 コンポーネントの作成に進みましょう。
ファイルアップロードコンポーネント
コードはApp.vue
で記述します。 ファイル内の自動生成されたコードをすべて削除します。
<!-- App.vue -->
<!-- HTML Template -->
<template>
<div id="app">
<div class="container">
<!--UPLOAD-->
<form enctype="multipart/form-data" novalidate v-if="isInitial || isSaving">
<h1>Upload images</h1>
<div class="dropbox">
<input type="file" multiple :name="uploadFieldName" :disabled="isSaving" @change="filesChange($event.target.name, $event.target.files); fileCount = $event.target.files.length"
accept="image/*" class="input-file">
<p v-if="isInitial">
Drag your file(s) here to begin<br> or click to browse
</p>
<p v-if="isSaving">
Uploading {{ fileCount }} files...
</p>
</div>
</form>
</div>
</template>
<!-- Javascript -->
<script>
</script>
<!-- SASS styling -->
<style lang="scss">
</style>
ノート:-
App.vue
コンポーネントは、テンプレート(HTML)、スクリプト(Javascript)、スタイル(SASS)の3つの部分で構成されています。- テンプレートにはアップロードフォームがあります。
- フォーム属性
enctype="multipart/form-data"
は重要です。 ファイルのアップロードを有効にするには、この属性を設定する必要があります。 enctype の詳細については、こちらをご覧ください。 - ファイルのアップロードを受け入れるためのファイル入力
<input type="file" />
があります。 プロパティmultiple
は、複数のファイルのアップロードを許可することを示します。 単一ファイルのアップロードのためにそれを削除します。 - ファイル入力
change
イベントを処理します。 ファイル入力が変更されると(誰かがファイルをドロップまたは選択する)、filesChange
関数をトリガーし、コントロール名と選択したファイル$event.target.files
を渡して、サーバーにアップロードします。 - ファイル入力を制限して、属性
accept="image/*"
を持つ画像のみを受け入れます。 - アップロード中はファイル入力が無効になるため、ユーザーはアップロードの完了後にのみファイルを再度ドロップ/選択できます。
- ファイルが変更されたときの
fileCount
をキャプチャします。Uploading {{ fileCount }} files...
をアップロードするファイルの数を表示する際に、fileCount
変数を使用します。
ファイルアップロードコンポーネントのスタイルを設定する
さて、それは興味深い部分です。 現在、コンポーネントは次のようになっています。
次のように変換する必要があります。
スタイリングしましょう!
<!-- App.vue -->
...
<!-- SASS styling -->
<style lang="scss">
.dropbox {
outline: 2px dashed grey; /* the dash box */
outline-offset: -10px;
background: lightcyan;
color: dimgray;
padding: 10px 10px;
min-height: 200px; /* minimum height */
position: relative;
cursor: pointer;
}
.input-file {
opacity: 0; /* invisible but it's there! */
width: 100%;
height: 200px;
position: absolute;
cursor: pointer;
}
.dropbox:hover {
background: lightblue; /* when mouse over to the drop zone, change color */
}
.dropbox p {
font-size: 1.2em;
text-align: center;
padding: 50px 0;
}
</style>
scssの数行だけで、コンポーネントはよりきれいに見えます。
ノート:-
opacity: 0
スタイルを適用して、ファイル入力を非表示にします。 これはファイル入力を非表示にするのではなく、ファイルを非表示にするだけです。- 次に、ファイル入力の親要素である
dropbox
cssクラスのスタイルを設定します。 ダッシュで囲まれたドロップファイルゾーンのように見せます。 - 次に、ドロップボックス内のテキストを中央に揃えます。
ファイルアップロードコンポーネントコード
コンポーネントのコーディングに進みましょう。
<!-- App.vue -->
...
<!-- Javascript -->
<script>
import { upload } from './file-upload.service';
const STATUS_INITIAL = 0, STATUS_SAVING = 1, STATUS_SUCCESS = 2, STATUS_FAILED = 3;
export default {
name: 'app',
data() {
return {
uploadedFiles: [],
uploadError: null,
currentStatus: null,
uploadFieldName: 'photos'
}
},
computed: {
isInitial() {
return this.currentStatus === STATUS_INITIAL;
},
isSaving() {
return this.currentStatus === STATUS_SAVING;
},
isSuccess() {
return this.currentStatus === STATUS_SUCCESS;
},
isFailed() {
return this.currentStatus === STATUS_FAILED;
}
},
methods: {
reset() {
// reset form to initial state
this.currentStatus = STATUS_INITIAL;
this.uploadedFiles = [];
this.uploadError = null;
},
save(formData) {
// upload data to the server
this.currentStatus = STATUS_SAVING;
upload(formData)
.then(x => {
this.uploadedFiles = [].concat(x);
this.currentStatus = STATUS_SUCCESS;
})
.catch(err => {
this.uploadError = err.response;
this.currentStatus = STATUS_FAILED;
});
},
filesChange(fieldName, fileList) {
// handle file changes
const formData = new FormData();
if (!fileList.length) return;
// append the files to FormData
Array
.from(Array(fileList.length).keys())
.map(x => {
formData.append(fieldName, fileList[x], fileList[x].name);
});
// save it
this.save(formData);
}
},
mounted() {
this.reset();
},
}
</script>
ノート:-
- コンポーネントにはいくつかのステータスがあります:STATUS_INITIAL、STATUS_SAVING、STATUS_SUCCESS、STATUS_FAILED、変数名はそれ自体かなり表現力があります。
- 後で、Hapi.jsファイルアップロードAPIを呼び出して画像をアップロードします。APIはフィールド呼び出し
photos
を受け入れます。 これがファイル入力フィールド名です。 - ファイルの変更は
filesChange
関数で処理します。FileList
HTMLのfilesプロパティによって返されるオブジェクトですエレメント。 これにより、で選択されたファイルのリストにアクセスできます。 エレメント。 詳細[こちら](( https://developer.mozilla.org/en/docs/Web/API/FileList )。 - 次に、新しい
FormData
を作成し、それにすべてのphotos
ファイルを追加します。FormData
インターフェースは、フォームフィールドとその値を表すキーと値のペアのセットを簡単に構築する方法を提供します。 詳細はこちらをご覧ください。 save
関数は、ファイルアップロードサービスを呼び出します(しばらくお待ちください。次にサービスを作成します)。 また、結果に応じてステータスを設定します。mount()
は、vueコンポーネントのライフサイクルフックです。 その間に、コンポーネントのステータスを初期状態に設定します。
ファイルアップロードサービス
サービスの作成に進みましょう。 axiosを使用してHTTP呼び出しを行います。
axiosをインストールします
# install axios
npm install axios --save
サービス
// file-upload.service.js
import * as axios from 'axios';
const BASE_URL = 'http://localhost:3001';
function upload(formData) {
const url = `${BASE_URL}/photos/upload`;
return axios.post(url, formData)
// get data
.then(x => x.data)
// add url field
.then(x => x.map(img => Object.assign({},
img, { url: `${BASE_URL}/images/${img.id}` })));
}
export { upload }
何もありませんが、コード自体はかなり表現力豊かです。 ファイルをアップロードし、結果を待ち、それに応じてマッピングします。
npm run dev
コマンドでアプリケーションを実行できます。 いくつかの画像をアップロードしてみてください、そしてそれはうまくいきます! (バックエンドサーバーを起動することを忘れないでください)
成功と失敗の結果を表示する
これでファイルを正常にアップロードできます。 ただし、UIには表示がありません。 HTMLテンプレートを更新しましょう。
<!-- App.vue -->
<!-- HTML Template -->
<template>
<div id="app">
<div class="container">
...form...
<!--SUCCESS-->
<div v-if="isSuccess">
<h2>Uploaded {{ uploadedFiles.length }} file(s) successfully.</h2>
<p>
<a href="javascript:void(0)" @click="reset()">Upload again</a>
</p>
<ul class="list-unstyled">
<li v-for="item in uploadedFiles">
<img :src="item.url" class="img-responsive img-thumbnail" :alt="item.originalName">
</li>
</ul>
</div>
<!--FAILED-->
<div v-if="isFailed">
<h2>Uploaded failed.</h2>
<p>
<a href="javascript:void(0)" @click="reset()">Try again</a>
</p>
<pre>{{ uploadError }}</pre>
</div>
</div>
</div>
</template>
ノート:-
- 正常にアップロードされたら、アップロードされた画像を表示します。
- アップロードに失敗したときにエラーメッセージを表示します。
フロントエンドでの偽のアップロード
ファイルのアップロードを処理するためにバックエンドアプリケーション(Hapi、Expressなど)を起動するのが面倒な場合。 これは、ファイルアップロードサービスを置き換える偽のサービスです。
// file-upload.fake.service.js
function upload(formData) {
const photos = formData.getAll('photos');
const promises = photos.map((x) => getImage(x)
.then(img => ({
id: img,
originalName: x.name,
fileName: x.name,
url: img
})));
return Promise.all(promises);
}
function getImage(file) {
return new Promise((resolve, reject) => {
const fReader = new FileReader();
const img = document.createElement('img');
fReader.onload = () => {
img.src = fReader.result;
resolve(getBase64Image(img));
}
fReader.readAsDataURL(file);
})
}
function getBase64Image(img) {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL('image/png');
return dataURL;
}
export { upload }
このStackoverflowpostでこのソリューションに出くわしました。 かなり便利です。 私のオンラインデモはこのサービスを使用しています。
基本的に、コードはソースを読み取り、それをキャンバスに描画し、canvastoDataURL
関数を使用してデータURLとして保存します。 キャンバスの詳細については、こちらをご覧ください。
これで、実際のサービスを偽のサービスと交換できます。
<!-- App.vue -->
...
<!-- Javascript -->
<script>
// swap as you need
import { upload } from './file-upload.fake.service'; // fake service
// import { upload } from './file-upload.service'; // real service
</script>
...
終わり! バックエンドAPIを停止し、ブラウザを更新します。アプリがまだ機能していることを確認し、代わりに偽のサービスを呼び出します。
ボーナス:約束を遅らせる
場合によっては、状態の変化を確認するために約束を遅らせたいことがあります。 この場合、ファイルのアップロードが速すぎる可能性があります。 そのためのヘルパー関数を書いてみましょう。
// utils.js
// utils to delay promise
function wait(ms) {
return (x) => {
return new Promise(resolve => setTimeout(() => resolve(x), ms));
};
}
export { wait }
その後、コンポーネントで使用できます
<!-- App.vue -->
...
<!-- Javascript -->
<script>
import { wait } from './utils';
...
save(formData) {
....
upload(formData)
.then(wait(1500)) // DEV ONLY: wait for 1.5s
.then(x => {
this.uploadedFiles = [].concat(x);
this.currentStatus = STATUS_SUCCESS;
})
...
},
</script>
結論
それでおしまい。 これは、Vueでサードパーティのライブラリやプラグインを使用せずにファイルのアップロードを処理する方法です。 そんなに大変じゃないですか?
ハッピーコーディング!
UI(フロントエンド)
- ソースコード: https://github.com/chybie/file-upload-vue
- デモ: https://vue-file-upload-1126b.firebaseapp.com/
API(バックエンド)チュートリアルとSourcode
- Hapi.jsを使用したファイルのアップロード: https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js 、または
- Express + Multerを使用したファイルのアップロード: https://scotch.io/tutorials/express-file-uploads-with-multer 、または
- 選択したクラウドソリューション(Amazon S3、Googleドライブなど)に切り替えます。