<input type="file">

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

型別為 type="file"<input> 元素允許使用者從其裝置儲存中選擇一個或多個檔案。選擇後,這些檔案可以使用表單提交上傳到伺服器,或者使用 JavaScript 程式碼和 檔案 API 進行處理。

試一試

<label for="avatar">Choose a profile picture:</label>

<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg" />
label {
  display: block;
  font:
    1rem "Fira Sans",
    sans-serif;
}

input,
label {
  margin: 0.4rem 0;
}

檔案輸入的 value 屬性包含一個字串,該字串表示所選檔案的路徑。如果尚未選擇檔案,則該值為空字串("")。當用戶選擇多個檔案時,value 表示他們選擇的檔案列表中的第一個檔案。其他檔案可以使用輸入的 HTMLInputElement.files 屬性來識別。

注意: 該值始終是檔名字首為 C:\fakepath\ 的檔名稱,而不是檔案的真實路徑。這是為了防止惡意軟體猜測使用者的檔案結構。

附加屬性

除了所有 <input> 元素共有的常見屬性外,file 型別的輸入還支援以下屬性。

accept

accept 屬性值是一個字串,它定義了檔案輸入應接受的檔案型別。此字串是以逗號分隔的唯一檔案型別說明符列表。由於給定的檔案型別可能以多種方式識別,因此當您需要給定格式的檔案時,提供一組完整的型別說明符會很有用。

例如,Microsoft Word 檔案可以透過多種方式識別,因此接受 Word 檔案的網站可以使用如下 <input>

html
<input
  type="file"
  id="docpicker"
  accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />

capture

capture 屬性值是一個字串,它指定用於捕獲影像或影片資料的攝像頭,如果 accept 屬性指示輸入應為這些型別之一。user 值表示應使用面向使用者的攝像頭和/或麥克風。environment 值指定應使用面向外部的攝像頭和/或麥克風。如果此屬性缺失,使用者代理可以自行決定如何操作。如果請求的朝向模式不可用,使用者代理可能會回退到其首選的預設模式。

注意: capture 以前是一個布林屬性,如果存在,則請求使用裝置的媒體捕獲裝置(例如攝像頭或麥克風)而不是請求檔案輸入。

multiple

當指定 multiple 布林屬性時,檔案輸入允許使用者選擇多個檔案。

非標準屬性

除了上面列出的屬性外,某些瀏覽器還提供以下非標準屬性。您應儘可能避免使用它們,因為這樣做會限制您的程式碼在不實現它們的瀏覽器中執行的能力。

webkitdirectory

布林屬性 webkitdirectory(如果存在)指示檔案選擇器介面中僅應允許使用者選擇目錄。有關其他詳細資訊和示例,請參見 HTMLInputElement.webkitdirectory

唯一檔案型別說明符

唯一檔案型別說明符是一個字串,它描述了使用者可以在型別為 file<input> 元素中選擇的檔案型別。每個唯一檔案型別說明符可以採用以下形式之一:

  • 一個有效的、不區分大小寫的檔名副檔名,以句點(“.”)字元開頭。例如:.jpg.pdf.doc
  • 一個有效的 MIME 型別字串,沒有副檔名。
  • 字串 audio/* 表示“任何音訊檔案”。
  • 字串 video/* 表示“任何影片檔案”。
  • 字串 image/* 表示“任何影像檔案”。

accept 屬性接受一個包含一個或多個這些唯一檔案型別說明符的字串作為其值,用逗號分隔。例如,需要呈現為影像的內容(包括標準影像格式和 PDF 檔案)的檔案選擇器可能如下所示:

html
<input type="file" accept="image/*,.pdf" />

使用檔案輸入

一個基本示例

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose file to upload</label>
    <input type="file" id="file" name="file" multiple />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

這會產生以下輸出:

注意: 您也可以在 GitHub 上找到此示例 — 請參閱原始碼,並檢視即時執行

無論使用者的裝置或作業系統如何,檔案輸入都提供一個按鈕,該按鈕會開啟一個檔案選擇器對話方塊,允許使用者選擇檔案。

如上所示,包含 multiple 屬性指定可以一次選擇多個檔案。使用者可以透過其所選平臺允許的任何方式(例如,按住 ShiftControl 然後單擊)從檔案選擇器中選擇多個檔案。如果您只希望使用者為每個 <input> 選擇一個檔案,請省略 multiple 屬性。

獲取所選檔案的資訊

所選檔案由元素的 HTMLInputElement.files 屬性返回,該屬性是一個 FileList 物件,其中包含一個 File 物件列表。FileList 的行為類似於陣列,因此您可以檢查其 length 屬性以獲取所選檔案的數量。

每個 File 物件包含以下資訊:

name

檔案的名稱。

lastModified

一個數字,指定檔案上次修改的日期和時間,以自 UNIX 紀元(1970 年 1 月 1 日午夜)以來的毫秒數表示。

lastModifiedDate 已棄用

一個 Date 物件,表示檔案上次修改的日期和時間。此屬性已棄用,不應使用。請改用 lastModified

size

檔案大小(以位元組為單位)。

type

檔案的 MIME 型別

webkitRelativePath 非標準

一個字串,指定檔案相對於在目錄選擇器中選擇的基目錄(即設定了 webkitdirectory 屬性的 file 選擇器)的路徑。此屬性為非標準屬性,應謹慎使用。

限制接受的檔案型別

通常,您不希望使用者能夠選擇任意型別的檔案;相反,您通常希望他們選擇特定型別或多種型別的檔案。例如,如果您的檔案輸入允許使用者上傳個人資料圖片,您可能希望他們選擇網路相容的影像格式,例如 JPEGPNG

可以使用 accept 屬性指定可接受的檔案型別,該屬性接受允許的副檔名或 MIME 型別的逗號分隔列表。一些示例:

  • accept="image/png"accept=".png" — 接受 PNG 檔案。
  • accept="image/png, image/jpeg"accept=".png, .jpg, .jpeg" — 接受 PNG 或 JPEG 檔案。
  • accept="image/*" — 接受任何 MIME 型別為 image/* 的檔案。(許多移動裝置在使用此屬性時也允許使用者使用攝像頭拍照。)
  • accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" — 接受任何像 MS Word 文件的檔案。

我們來看一個更完整的例子:

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="profile_pic">Choose file to upload</label>
    <input
      type="file"
      id="profile_pic"
      name="profile_pic"
      accept=".jpg, .jpeg, .png" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

這會產生與上一個示例類似的輸出:

注意: 您也可以在 GitHub 上找到此示例 — 請參閱原始碼,並檢視即時執行

它看起來可能相似,但如果您嘗試使用此輸入選擇檔案,您會看到檔案選擇器只允許您選擇 accept 值中指定的檔案型別(確切的介面因瀏覽器和作業系統而異)。

accept 屬性不驗證所選檔案的型別;它為瀏覽器提供提示,以引導使用者選擇正確的檔案型別。在大多數情況下,使用者仍然可以在檔案選擇器中切換一個選項,使其能夠覆蓋此限制並選擇他們想要的任何檔案,然後選擇不正確的檔案型別。

因此,您應該確保 accept 屬性由適當的伺服器端驗證支援。

檢測取消

當用戶未更改其選擇,重新選擇之前選擇的檔案時,會觸發 cancel 事件。當檔案選擇器對話方塊透過“取消”按鈕或 escape 鍵關閉或取消時,也會觸發 cancel 事件。

例如,如果使用者在未選擇檔案的情況下關閉彈出視窗,以下程式碼將記錄到控制檯:

js
const elem = document.createElement("input");
elem.type = "file";
elem.addEventListener("cancel", () => {
  console.log("Cancelled.");
});
elem.addEventListener("change", () => {
  if (elem.files.length === 1) {
    console.log("File selected: ", elem.files[0]);
  }
});
elem.click();

注意

  1. 您無法透過指令碼設定檔案選擇器的值——執行以下操作無效:

    js
    const input = document.querySelector("input[type=file]");
    input.value = "foo";
    
  2. 當使用 <input type="file"> 選擇檔案時,出於顯而易見的安全原因,實際的原始檔路徑不會顯示在輸入的 value 屬性中。相反,會顯示檔名,並附帶 C:\fakepath\ 字首。這個怪癖有一些歷史原因,但它在所有現代瀏覽器中都受支援,事實上,它在規範中有所定義

示例

在此示例中,我們將展示一個稍微更高階的檔案選擇器,它利用了 HTMLInputElement.files 屬性中可用的檔案資訊,並展示了一些巧妙的技巧。

注意: 您可以在 GitHub 上檢視此示例的完整原始碼 — file-example.html也可在此處檢視即時執行)。我們不會解釋 CSS;JavaScript 是主要焦點。

首先,我們來看一下 HTML:

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="image_uploads">Choose images to upload (PNG, JPG)</label>
    <input
      type="file"
      id="image_uploads"
      name="image_uploads"
      accept=".jpg, .jpeg, .png"
      multiple />
  </div>
  <div class="preview">
    <p>No files currently selected for upload</p>
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

這與我們之前看到的類似——沒有什麼特別需要評論的。

接下來,我們來講解 JavaScript。

在指令碼的前幾行,我們獲取了表單輸入本身以及類名為 .preview<div> 元素的引用。接下來,我們隱藏了 <input> 元素 — 我們這樣做是因為檔案輸入往往醜陋、難以樣式化,並且在不同瀏覽器中的設計不一致。您可以透過單擊其 <label> 來啟用 input 元素,因此最好在視覺上隱藏 input 並將標籤樣式化為按鈕,以便使用者知道如果他們想要上傳檔案,可以與它進行互動。

js
const input = document.querySelector("input");
const preview = document.querySelector(".preview");

input.style.opacity = 0;

注意: 使用 opacity 來隱藏檔案輸入,而不是 visibility: hiddendisplay: none,因為輔助技術將後兩種樣式解釋為檔案輸入不可互動。

接下來,我們向輸入新增一個事件監聽器,以監聽其所選值的更改(在此例中,當檔案被選中時)。事件監聽器會呼叫我們自定義的 updateImageDisplay() 函式。

js
input.addEventListener("change", updateImageDisplay);

每當呼叫 updateImageDisplay() 函式時,我們都會:

  • 使用 while 迴圈清空預覽 <div> 的先前內容。

  • 獲取包含所有選定檔案資訊的 FileList 物件,並將其儲存在一個名為 curFiles 的變數中。

  • 透過檢查 curFiles.length 是否等於 0 來檢視是否未選擇任何檔案。如果是,則在預覽 <div> 中列印一條訊息,說明尚未選擇檔案。

  • 如果選擇檔案,我們會迴圈遍歷每個檔案,將其資訊列印到預覽 <div> 中。這裡需要注意的事項:

  • 我們使用自定義的 validFileType() 函式來檢查檔案是否為正確型別(例如,accept 屬性中指定的影像型別)。

  • 如果是,我們:

    • 將其名稱和檔案大小列印到前一個 <div> 內的列表項中(從 file.namefile.size 獲取)。自定義的 returnFileSize() 函式返回一個格式良好的大小版本,以位元組/KB/MB 為單位(預設情況下,瀏覽器以絕對位元組報告大小)。
    • 透過呼叫 URL.createObjectURL(file) 生成影像的縮圖預覽。然後,透過建立一個新的 <img> 並將其 src 設定為縮圖,將影像也插入到列表項中。
  • 如果檔案型別無效,我們會在列表項中顯示一條訊息,告知使用者需要選擇不同的檔案型別。

js
function updateImageDisplay() {
  while (preview.firstChild) {
    preview.removeChild(preview.firstChild);
  }

  const curFiles = input.files;
  if (curFiles.length === 0) {
    const para = document.createElement("p");
    para.textContent = "No files currently selected for upload";
    preview.appendChild(para);
  } else {
    const list = document.createElement("ol");
    preview.appendChild(list);

    for (const file of curFiles) {
      const listItem = document.createElement("li");
      const para = document.createElement("p");
      if (validFileType(file)) {
        para.textContent = `File name ${file.name}, file size ${returnFileSize(
          file.size,
        )}.`;
        const image = document.createElement("img");
        image.src = URL.createObjectURL(file);
        image.alt = image.title = file.name;

        listItem.appendChild(image);
        listItem.appendChild(para);
      } else {
        para.textContent = `File name ${file.name}: Not a valid file type. Update your selection.`;
        listItem.appendChild(para);
      }

      list.appendChild(listItem);
    }
  }
}

自定義的 validFileType() 函式接受一個 File 物件作為引數,然後使用 Array.prototype.includes() 檢查 fileTypes 中的任何值是否與檔案的 type 屬性匹配。如果找到匹配項,函式返回 true。如果未找到匹配項,則返回 false

js
// https://mdn.club.tw/en-US/docs/Web/Media/Guides/Formats/Image_types
const fileTypes = [
  "image/apng",
  "image/bmp",
  "image/gif",
  "image/jpeg",
  "image/pjpeg",
  "image/png",
  "image/svg+xml",
  "image/tiff",
  "image/webp",
  "image/x-icon",
];

function validFileType(file) {
  return fileTypes.includes(file.type);
}

returnFileSize() 函式接受一個數字(位元組數,取自當前檔案的 size 屬性),並將其轉換為格式良好的位元組/KB/MB 大小。

js
function returnFileSize(number) {
  if (number < 1e3) {
    return `${number} bytes`;
  } else if (number >= 1e3 && number < 1e6) {
    return `${(number / 1e3).toFixed(1)} KB`;
  }
  return `${(number / 1e6).toFixed(1)} MB`;
}

注意: 這裡的“KB”和“MB”單位使用的是 SI 字首約定,即 1KB = 1000B,類似於 macOS。不同的系統以不同的方式表示檔案大小——例如,Ubuntu 使用 IEC 字首,其中 1KiB = 1024B,而 RAM 規範通常使用 SI 字首來表示 2 的冪(1KB = 1024B)。因此,我們使用 1e31000)和 1e6100000)而不是 10241048576。在您的應用程式中,如果確切大小很重要,您應該清楚地向用戶說明單位系統。

該示例看起來像這樣;試玩一下:

技術摘要

一個表示所選檔案路徑的字串。
事件 change, inputcancel
支援的常見屬性 required
附加屬性 accept, capture, multiple
IDL 屬性 filesvalue
DOM 介面 HTMLInputElement
方法 select()
隱式 ARIA 角色 沒有對應的角色

規範

規範
HTML
# file-upload-state-(type=file)

瀏覽器相容性

另見