點燈坊

失くすものさえない今が強くなるチャンスよ

實作 File Upload

Sam Xiao's Avatar 2021-04-10

若要上傳檔案,最簡單就是使用 HTML 的 <input type=file>,但 HTML 的 <input> 顯示太過陽春,不太符合目前主流 Web 顯示,當然可以找 Package 達成需求,事實上以 Tailwind CSS 就能簡單實作出漂亮的 File Upload。

Version

Vue 3.0.5
Tailwind CSS 2.1.1

HTML

upload000

原生 HTML 的結果非常陽春。

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

第 3 行

<input type="file">

只使用 HTML 的 <input type="file">

Tailwind CSS

upload001

改用 button 實現 file upload,可用千變萬化的 CSS 控制。

<template>
  <input ref="fileUpload" type="file" accept="images/*" multiple hidden @input="onInput">
  <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload()">
    Upload
  </button>
  <p>{{ name }}</p>
</template>

<script setup>
ref: name = ''
ref: fileUpload = null

let onUpload = () => fileUpload.click()

let onInput = () => {
  let files = fileUpload.files

  for (let x of files)
    name = x.name
}
</script>

第 2 行

<input ref="fileUpload" type="file" hidden>

一樣使用 HTML 的 <input type="file">,但加上了 hidden 使其隱藏不顯示,另外加上了 ref="fileUpload" 供 Vue 使用。

第 3 行

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload()">Upload</button>

使用 HTML 的 <button> 搭配一堆 Tailwind CSS 的 utility 修飾:

  • bg-blue-500:設定 button 背景為藍色
  • hover-blue-700:設定當 button hover 時,背景會變為稍為深的藍色
  • text-white:設定文字為白色
  • font-bold:設定文字為粗體
  • py-2:設定 Y 軸 padding
  • px-4:設定 X 軸 padding,一般來說 button 會設定 X 軸 padding 較大
  • rounded:設定 button 有圓角

click 時執行 onUpload()

第 6 行

<p> {{ name }} </p>

下方顯示檔案名稱。

11 行

ref: fileUpload = null

fileUpload 需使用 ref 設定成 null

13 行

let onUpload = () => fileUpload.click()

當 button click 時,執行 fileUpload.click()

15 行

let onInput = () => {
  let files = fileUpload.files

  for (let x of files)
    name = x.name
}

fileUpload.click() 執行時是會執行 onInput()

使用 fileUpload.files 取得所有檔案,其為 Object Array,可由 name 取得 file name 。

Function Pipeline

upload001

改用 Function Pipeline 方式實現。

<template>
  <input ref="fileUpload" type="file" accept="images/*" multiple hidden @input="onInput">
  <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" @click="onUpload">
    Upload
  </button>
  <p>{{ name }}</p>
</template>

<script setup>
import { ref } from 'vue'
import { pipe, prop, forEach } from 'ramda'
import { read, write } from 'vue3-fp'

let name = ref('')
let fileUpload = ref(null)

let onUpload = pipe(
  read(fileUpload),
  x => x.click()
)

let showName = pipe(
  prop('name'),
  write(name)
)

let onInput = pipe(
  read(fileUpload),
  prop('files'),
  forEach(showName),
)
</script>

14 行

let name = ref('')
let fileUpload = ref(null)

Function Pipeline 適合以 ref() 定義出始值。

17 行

let onUpload = pipe(
  read(fileUpload),
  x => x.click()
)

組合出當 button click 時的 onUpload()

  • read(fileUpload):讀出 fileUpload ref
  • x => x.click():執行 fileUpload.click()

27 行

let onInput = pipe(
  read(fileUpload),
  prop('files'),
  forEach(showName),
)

組合出 <input type="file">onInput()

  • read(fileUpload):讀出 fileUpload ref
  • prop('files'):讀出 files prop
  • forEach(showName):顯示所有檔案名稱

22 行

let showName = pipe(
  prop('name'),
  write(name)
)

顯示單一檔案名稱:

  • prop('name'):讀出 name prop
  • write(name):寫入 name state

Conclusion

  • File upload 其實不須另外找 package,只要 HTML 的 <input type="file"><button> 加上 Tailwind CSS 修飾即可