JavaScript
Vue.js
3点リーダー

Vue.jsでファイルアップロードの実装--ついでにプレビュー機能も実装した話--

画像選択画面録画.gif


ファイルアップロードのコンポーネントを作成

スタートから悩み悩みやってみた結果、一応目標物はできたのでアウトプット。


☆Special thanks☆

input type="file"のボタンデザインを変更する方法

javascriptで文字列をバイト(bytes)単位で省略する方法


初手


index.vue

<template>

<div>
<label>
<input type="file" placeholder="何でやねん" />
</label>
</div>
</template>

これ実行してみると

image.png


デフォルトのプレイスフォルダーを変更したい

ファイルを選択という箇所。

何でやねん

が表示されてないようだ。

これは自由の変えられないのかな?と悩んでいたら。書く場所を間違っていました。

placeholderはinputへ書くものじゃないらしい。

デフォルトのスタイルを変更するには、


index.vue

<label>画像を選択

<input />
</label>

このようにlabelタグへ追加することで表示されるようになる。

(クリックするとファイルを選択と同様に動くからこれで問題ない)

image.png

あれ、ファイルを選択するが消えてないけど?

と思って調べていたら、プレイスホルダーを変更するというよりは、

元々のデザインをなかったことにして新たに作成するというイメージらしい。

ファイル選択選択されていません

という不要な部分を削除するには、スタイルを当てるしかない。


display: none;でスタイルを削除し、labelに対して新たにスタイルを当てる


index.vue

<template>

<div class="input-item">
<label class="input-item__label">
画像を選択
<input type="file" />
</label>
</div>
</template>

<style>
label > input {
display: none;
}

label {
padding: 0 1rem;
border: solid 1px #888;
}

label::after {
content: '+';
font-size: 1rem;
color: #888;
padding-left: 1rem;
}
</style>


image.png

若干不恰好だけどできました。


せっかくだし選択した画像をプレビューできる機能をつける

画像を選択すると下の画像のように、選択した画像&画像ファイル名が表示されるようなプレビュー機能も実装してみます。

プレビュー機能実装

やりたいことは5つ

・選択したら画像が表示される

・選択した画像ファイル名が表示される

・画像が表示されたら画像選択のボタンが消える

・画像ファイル名が表示される

・画像ファイルのcloseをクリックするともう一度画像を選択ボタンが表示される


選択した画像表示する場所と表示methodsを実装する


index.vue

<template>

<div class="contents">
<label v-show="!uploadedImage" class="input-item__label"
>画像を選択
<input type="file" @change="onFileChange" />
</label>
<div class="preview-item">
<img
v-show="uploadedImage"
class="preview-item-file"
:src="uploadedImage"
alt=""
/>
<div v-show="uploadedImage" class="preview-item-btn" @click="remove">
<p class="preview-item-name">{{ img_name }}</p>
<e-icon class="preview-item-icon">close</e-icon>
</div>
</div>
</div>
</template>

<script>
import EIcon from '../components/EIcon.vue';

export default {
components: {
EIcon,
},
data() {
return {
uploadedImage: '',
img_name: '',
};
},
methods: {
onFileChange(e) {
const files = e.target.files || e.dataTransfer.files;
this.createImage(files[0]);
this.img_name = files[0].name;
},
// アップロードした画像を表示
createImage(file) {
const reader = new FileReader();
reader.onload = e => {
this.uploadedImage = e.target.result;
};
reader.readAsDataURL(file);
},
remove() {
this.uploadedImage = false;
},
},
};
</script>


これで、

・選択したら画像が表示される

・選択した画像ファイル名が表示される

・画像が表示されたら画像選択のボタンが消える

・画像ファイル名が表示される

・画像ファイルのcloseをクリックするともう一度画像を選択ボタンが表示される

実はこれで5つは実装できましたが、現状では長いファイル名の場合、折り返してしまいデザインが崩れてしまいました。

image.png

この通り、ファイル名が長い時デザインが崩れてしまうため、短くカットしてデザイン崩れしないようにしたいと思います。


3点リーダー、拡張子残し、全角、半角の判別を組み合わせてファイル名表示を工夫する


onFileChange(e) {
const files = e.target.files || e.dataTransfer.files;
this.createImage(files[0]);
// 拡張子で分ける(※.が1つの想定です)
const imgNameExe = files[0].name.split('.');

// 拡張子から前
let imgName = imgNameExe[0];

// 拡張子から後ろ
const imgExe = imgNameExe[1];

// 表示したいMaxのByte数(全角10文字、半角20文字)
const maxBytes = 20;
const imgNameBytes = encodeURIComponent(imgName).replace(/%../g, 'x').length;

// 画像ファイルとMax Byte数の比較
if (imgNameBytes > maxBytes) {
const zenkaku = imgNameBytes - imgName.length;
if (zenkaku > 0) {
imgName = imgName.slice(0, maxBytes / 2 - imgName.length) + '..';
} else {
imgName = imgName.slice(0, maxBytes - imgNameBytes) + '..';
}
}

// 短くカットしたものと.と拡張子の文字列の連結
imgName = imgName + '.' + imgExe;
this.img_name = imgName;
},

こうして実装ができました。

画像選択画面録画.gif

現在、長いファイル名の表示が、XXX...png などの表示になっているけれど、おそらくXXX...XXX.pngのようにファイル名の先頭と末尾が見えるような工夫が必要そうです。

ですが、今回はここまで!