文件上傳,搞懂這 8 種場景就夠了

在日常工作中,文件上傳是一個很常見的功能。在項目開發過程中,我們通常都會使用一些成熟的上傳組件來實現對應的功能。一般來說,成熟的上傳組件不僅會提供漂亮 UI 或好的交互體驗,而且還會提供多種不同的上傳方式,以滿足不同的場景需求。

一般在我們工作中,主要會涉及到 8 種文件上傳的場景,每一種場景背後都使用不同的技術,其中也有很多細節需要我們額外注意。今天阿寶哥就來帶大家總結一下這 8 種場景,讓大家能更好地理解成熟上傳組件所提供的功能。閱讀本文後,你將會了解以下的內容:

一、單文件上傳

對於單文件上傳的場景來說,最常見的是圖片上傳的場景,所以我們就以圖片上傳爲例,先來介紹單文件上傳的基本流程。

1.1 前端代碼

html

在以下代碼中,我們通過 input 元素的 accept 屬性限制了上傳文件的類型。這裏使用 image/* 限制只能選擇圖片文件,當然你也可以設置特定的類型,比如 image/pngimage/png,image/jpeg

<input type="file" accept="image/*" />
<button onclick="uploadFile()">上傳文件</button>
複製代碼

需要注意的是,雖然我們把 input 元素的 accept 屬性設置爲 image/png。但如果用戶把 jpg/jpeg 格式的圖片後綴名改爲 .png,就可以成功繞過這個限制。要解決這個問題,我們可以通過讀取文件中的二進制數據來識別正確的文件類型。

要查看圖片對應的二進制數據,我們可以藉助一些現成的編輯器,比如 Windows 平臺下的 WinHex 或 macOS 平臺下的 Synalyze It! Pro 十六進制編輯器。這裏我們使用 Synalyze It! Pro 這個編輯器,來查看阿寶哥頭像對應的二進制數據。

那麼在前端能否不借助工具,讀取文件的二進制數據呢?答案是可以的,這裏阿寶哥就不展開介紹了。感興趣的話,你可以閱讀 JavaScript 如何檢測文件的類型? 這篇文章。另外,需要注意的是 input 元素 accept 屬性有存在兼容性問題。比如 IE 9 以下不支持,具體如下圖所示:

(圖片來源 —— caniuse.com/input-file-…

js

const uploadFileEle = document.querySelector("#uploadFile");

const request = axios.create({
  baseURL: "http://localhost:3000/upload",
  timeout: 60000, 
});

async function uploadFile() {
  if (!uploadFileEle.files.length) return;
  const file = uploadFileEle.files[0]; 
  
  upload({
    url: "/single",
    file,
  });
}

function upload({ url, file, fieldName = "file" }) {
  let formData = new FormData();
  formData.set(fieldName, file);
  request.post(url, formData, {
    
    onUploadProgress: function (progressEvent) {
      const percentCompleted = Math.round(
        (progressEvent.loaded * 100) / progressEvent.total
      );
      console.log(percentCompleted);
     },
  });
}
複製代碼

在以上代碼中,我們先把讀取的 File 對象封裝成 FormData 對象,然後利用 Axios 實例的 post 方法實現文件上傳的功能。 在上傳前,通過設置請求配置對象的 onUploadProgress 屬性,就可以獲取文件的上傳進度。

1.2 服務端代碼

Koa 是一個簡單易用的 Web 框架,它的特點是優雅、簡潔、輕量、自由度高。所以我們選擇它來搭建文件服務,並使用以下中間件來實現相應的功能:

const path = require("path");
const Koa = require("koa");
const serve = require("koa-static");
const cors = require("@koa/cors");
const multer = require("@koa/multer");
const Router = require("@koa/router");

const app = new Koa();
const router = new Router();
const PORT = 3000;

const RESOURCE_URL = `http://localhost:${PORT}`;

const UPLOAD_DIR = path.join(__dirname, "/public/upload");

const storage = multer.diskStorage({
  destination: async function (req, file, cb) {
    
    cb(null, UPLOAD_DIR);
  },
  filename: function (req, file, cb) {
    
    cb(null, `${file.originalname}`);
  },
});

const multerUpload = multer({ storage });

router.get("/", async (ctx) => {
  ctx.body = "歡迎使用文件服務(by 阿寶哥)";
});

router.post(
  "/upload/single",
  async (ctx, next) => {
    try {
      await next();
      ctx.body = {
        code: 1,
        msg: "文件上傳成功",
        url: `${RESOURCE_URL}/${ctx.file.originalname}`,
      };
    } catch (error) {
      ctx.body = {
        code: 0,
        msg: "文件上傳失敗"
      };
    }
  },
  multerUpload.single("file")
);


app.use(cors());
app.use(serve(UPLOAD_DIR));
app.use(router.routes()).use(router.allowedMethods());

app.listen(PORT, () => {
  console.log(`app starting at port ${PORT}`);
});
複製代碼

以上代碼相對比較簡單,我們就不展開介紹了。Koa 內核很簡潔,擴展功能都是通過中間件來實現。比如示例中使用到的路由、CORS、靜態資源處理等功能都是通過中間件實現。因此要想掌握 Koa 這個框架,核心是掌握它的中間件機制。如果你想深入瞭解的話,可以閱讀 如何更好地理解中間件和洋蔥模型 這篇文章。其實除了單文件上傳外,在文件上傳的場景中,我們也可以同時上傳多個文件。

單文件上傳示例:single-file-upload

github.com/semlinker/f…

二、多文件上傳

要上傳多個文件,首先我們需要允許用戶同時選擇多個文件。要實現這個功能,我們可以利用 input 元素的 multiple 屬性。跟前面介紹的 accept 屬性一樣,該屬性也存在兼容性問題,具體如下圖所示:

(圖片來源 —— caniuse.com/mdn-api_htm…

2.1 前端代碼

html

相比單文件上傳的代碼,多文件上傳場景下的 input 元素多了一個 multiple 屬性:

<input type="file" accept="image/*" multiple />
<button onclick="uploadFile()">上傳文件</button>
複製代碼

js

在單文件上傳的代碼中,我們通過 uploadFileEle.files[0] 獲取單個文件,而對於多文件上傳來說,我們需要獲取已選擇的文件列表,即通過 uploadFileEle.files 來獲取,它返回的是一個 FileList 對象。

async function uploadFile() {
  if (!uploadFileEle.files.length) return;
  const files = Array.from(uploadFileEle.files);
  upload({
    url: "/multiple",
    files,
  });
}
複製代碼

因爲要支持上傳多個文件,所以我們需要同步更新一下 upload 函數。對應的處理邏輯就是遍歷文件列表,然後使用 FormData 對象的 append 方法來添加多個文件,具體代碼如下所示:

function upload({ url, files, fieldName = "file" }) {
  let formData = new FormData();
  files.forEach((file) => {
    formData.append(fieldName, file);
  });
  request.post(url, formData, {
    
    onUploadProgress: function (progressEvent) {
      const percentCompleted = Math.round(
        (progressEvent.loaded * 100) / progressEvent.total
      );
      console.log(percentCompleted);
    },
  });
}
複製代碼

2.2 服務端代碼

在以下代碼中,我們定義了一個新的路由 —— /upload/multiple 來處理多文件上傳的功能。當所有文件都成功上傳後,就會返回一個已上傳文件的 url 地址列表:

router.post(
  "/upload/multiple",
  async (ctx, next) => {
    try {
      await next();
      urls = ctx.files.file.map(file => `${RESOURCE_URL}/${file.originalname}`);
      ctx.body = {
        code: 1,
        msg: "文件上傳成功",
        urls
      };
    } catch (error) {
      ctx.body = {
        code: 0,
        msg: "文件上傳失敗",
      };
    }
  },
  multerUpload.fields([
    {
      name: "file", 
    },
  ])
);
複製代碼

介紹完單文件和多文件上傳的功能,接下來我們來介紹目錄上傳的功能。

多文件上傳示例:multiple-file-upload

github.com/semlinker/f…

三、目錄上傳

可能你還不知道,input 元素上還有一個的 webkitdirectory 屬性。當設置了 webkitdirectory 屬性之後,我們就可以選擇目錄了。

<input type="file" accept="image/*" webkitdirectory />
複製代碼

當我們選擇了指定目錄之後,比如阿寶哥桌面上的 images 目錄,就會顯示以下確認框:

點擊上傳按鈕之後,我們就可以獲取文件列表。列表中的文件對象上含有一個 webkitRelativePath 屬性,用於表示當前文件的相對路徑。

雖然通過 webkitdirectory 屬性可以很容易地實現選擇目錄的功能,但在實際項目中我們還需要考慮它的兼容性。比如在 IE 11 以下的版本就不支持該屬性,其它瀏覽器的兼容性如下圖所示:

(圖片來源 —— caniuse.com/?search=web…

瞭解完 webkitdirectory 屬性的兼容性,我們先來介紹前端的實現代碼。

3.1 前端代碼

爲了讓服務端能按照實際的目錄結構來存放對應的文件,在添加表單項時我們需要把當前文件的路徑提交到服務端。此外,爲了確保@koa/multer 能正確處理文件的路徑,我們需要對路徑進行特殊處理。即把 / 斜槓替換爲 @ 符號。對應的處理方式如下所示:

function upload({ url, files, fieldName = "file" }) {
  let formData = new FormData();
  files.forEach((file, i) => {
    formData.append(
      fieldName, 
      files[i],
      files[i].webkitRelativePath.replace(/\//g, "@");
    );
  });
  request.post(url, formData); 
}
複製代碼

3.2 服務端代碼

目錄上傳與多文件上傳,服務端代碼的主要區別就是 @koa/multer 中間件的配置對象不一樣。在 destination 屬性對應的函數中,我們需要把文件名中 @ 還原成 /,然後根據文件的實際路徑來生成目錄。

const fse = require("fs-extra");
const storage = multer.diskStorage({
  destination: async function (req, file, cb) {
    
    let relativePath = file.originalname.replace(/@/g, path.sep);
    let index = relativePath.lastIndexOf(path.sep);
    let fileDir = path.join(UPLOAD_DIR, relativePath.substr(0, index));
    
    await fse.ensureDir(fileDir); 
    cb(null, fileDir);
  },
  filename: function (req, file, cb) {
    let parts = file.originalname.split("@");
    cb(null, `${parts[parts.length - 1]}`); 
  },
});
複製代碼

現在我們已經實現了目錄上傳的功能,那麼能否把目錄下的文件壓縮成一個壓縮包後再上傳呢?答案是可以的,接下來我們來介紹如何實現壓縮目錄上傳的功能。

目錄上傳示例:directory-upload

github.com/semlinker/f…

四、壓縮目錄上傳

JavaScript 如何在線解壓 ZIP 文件? 這篇文章中,介紹了在瀏覽器端如何使用 JSZip 這個庫實現在線解壓 ZIP 文件的功能。 JSZip 這個庫除了可以解析 ZIP 文件之外,它還可以用來 創建和編輯 ZIP 文件。利用 JSZip 這個庫提供的 API,我們就可以把目錄下的所有文件壓縮成 ZIP 文件,然後再把生成的 ZIP 文件上傳到服務器。

4.1 前端代碼

JSZip 實例上的 file(name, data [,options]) 方法,可以把文件添加到 ZIP 文件中。基於該方法我們可以封裝了一個 generateZipFile 函數,用於把目錄下的文件列表壓縮成一個 ZIP 文件。以下是 generateZipFile 函數的具體實現:

function generateZipFile(
  zipName, files,
  options = { type: "blob", compression: "DEFLATE" }
) {
  return new Promise((resolve, reject) => {
    const zip = new JSZip();
    for (let i = 0; i < files.length; i++) {
      zip.file(files[i].webkitRelativePath, files[i]);
    }
    zip.generateAsync(options).then(function (blob) {
      zipName = zipName || Date.now() + ".zip";
      const zipFile = new File([blob], zipName, {
        type: "application/zip",
      });
      resolve(zipFile);
    });
  });
}
複製代碼

在創建完 generateZipFile 函數之後,我們需要更新一下前面已經介紹過的 uploadFile 函數:

async function uploadFile() {
  let fileList = uploadFileEle.files;
  if (!fileList.length) return;
  let webkitRelativePath = fileList[0].webkitRelativePath;
  let zipFileName = webkitRelativePath.split("/")[0] + ".zip";
  let zipFile = await generateZipFile(zipFileName, fileList);
  upload({
    url: "/single",
    file: zipFile,
    fileName: zipFileName
  });
}
複製代碼

在以上的 uploadFile 函數中,我們會對返回的 FileList 對象進行處理,即調用 generateZipFile 函數來生成 ZIP 文件。此外,爲了在服務端接收壓縮文件時,能獲取到文件名,我們爲 upload 函數增加了一個 fileName 參數,該參數用於調用 formData.append 方法時,設置上傳文件的文件名:

function upload({ url, file, fileName, fieldName = "file" }) {
  if (!url || !file) return;
  let formData = new FormData();
  formData.append(
    fieldName, file, fileName
  );
  request.post(url, formData); 
}
複製代碼

以上就是壓縮目錄上傳,前端部分的 JS 代碼,服務端的代碼可以參考前面單文件上傳的相關代碼。

壓縮目錄上傳示例:directory-compress-upload

github.com/semlinker/f…

五、拖拽上傳

要實現拖拽上傳的功能,我們需要先了解與拖拽相關的事件。比如 dragdragenddragenterdragoverdrop 事件等。這裏我們只介紹接下來要用到的拖拽事件:

基於上面的這些事件,我們就可以提高用戶拖拽的體驗。比如當用戶拖拽的元素進入目標區域時,對目標區域進行高亮顯示。當用戶拖拽的元素離開目標區域時,移除高亮顯示。很明顯當 drop 事件觸發後,拖拽的元素已經放入目標區域了,這時我們就需要獲取對應的數據。

那麼如何獲取拖拽對應的數據呢?這時我們需要使用 DataTransfer 對象,該對象用於保存拖動並放下過程中的數據。它可以保存一項或多項數據,這些數據項可以是一種或者多種數據類型。若拖動操作涉及拖動文件,則我們可以通過 DataTransfer 對象的 files 屬性來獲取文件列表。

介紹完拖拽上傳相關的知識後,我們來看一下具體如何實現拖拽上傳的功能。

5.1 前端代碼

html

<div>
   <p>拖拽上傳文件</p>
   <div></div>
</div>
複製代碼

css

#dropArea {
  width: 300px;
  height: 300px;
  border: 1px dashed gray;
  margin-bottom: 20px;
}
#dropArea p {
  text-align: center;
  color: #999;
}
#dropArea.highlighted {
  background-color: #ddd;
}
#imagePreview {
  max-height: 250px;
  overflow-y: scroll;
}
#imagePreview img {
  width: 100%;
  display: block;
  margin: auto;
}
複製代碼

js

爲了讓大家能夠更好地閱讀拖拽上傳的相關代碼,我們把代碼拆成 4 部分來講解:

1、阻止默認拖拽行爲

const dropAreaEle = document.querySelector("#dropArea");
const imgPreviewEle = document.querySelector("#imagePreview");
const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png)$/i;

["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
   dropAreaEle.addEventListener(eventName, preventDefaults, false);
   document.body.addEventListener(eventName, preventDefaults, false);
});

function preventDefaults(e) {
  e.preventDefault();
  e.stopPropagation();
}
複製代碼

2、切換目標區域的高亮狀態

["dragenter", "dragover"].forEach((eventName) => {
    dropAreaEle.addEventListener(eventName, highlight, false);
});
["dragleave", "drop"].forEach((eventName) => {
    dropAreaEle.addEventListener(eventName, unhighlight, false);
});


function highlight(e) {
  dropAreaEle.classList.add("highlighted");
}


function unhighlight(e) {
  dropAreaEle.classList.remove("highlighted");
}
複製代碼

3、處理圖片預覽

dropAreaEle.addEventListener("drop", handleDrop, false);

function handleDrop(e) {
  const dt = e.dataTransfer;
  const files = [...dt.files];
  files.forEach((file) => {
    previewImage(file, imgPreviewEle);
  });
  
}

function previewImage(file, container) {
  if (IMAGE_MIME_REGEX.test(file.type)) {
    const reader = new FileReader();
    reader.onload = function (e) {
      let img = document.createElement("img");
      img.src = e.target.result;
      container.append(img);
    };
    reader.readAsDataURL(file);
  }
}
複製代碼

4、文件上傳

function handleDrop(e) {
  const dt = e.dataTransfer;
  const files = [...dt.files];
  
  files.forEach((file) => {
    upload({
      url: "/single",
      file,
    });
  });
}

const request = axios.create({
  baseURL: "http://localhost:3000/upload",
  timeout: 60000,
});

function upload({ url, file, fieldName = "file" }) {
  let formData = new FormData();
  formData.set(fieldName, file);
  request.post(url, formData, {
    
    onUploadProgress: function (progressEvent) {
      const percentCompleted = Math.round(
        (progressEvent.loaded * 100) / progressEvent.total
      );
      console.log(percentCompleted);
    },
  });
}
複製代碼

拖拽上傳算是一個比較常見的場景,很多成熟的上傳組件都支持該功能。其實除了拖拽上傳外,還可以利用剪貼板實現複製上傳的功能。

拖拽上傳示例:drag-drop-upload

github.com/semlinker/f…

六、剪貼板上傳

在介紹如何實現剪貼板上傳的功能前,我們需要了解一下 Clipboard API。Clipboard 接口實現了 Clipboard API,如果用戶授予了相應的權限,就能提供系統剪貼板的讀寫訪問。在 Web 應用程序中,Clipboard API 可用於實現剪切、複製和粘貼功能。該 API 用於取代通過 document.execCommand API 來實現剪貼板的操作。

在實際項目中,我們不需要手動創建 Clipboard 對象,而是通過 navigator.clipboard 來獲取 Clipboard 對象:

在獲取 Clipboard 對象之後,我們就可以利用該對象提供的 API 來訪問剪貼板,比如:

navigator.clipboard.readText().then(
  clipText => document.querySelector(".editor").innerText = clipText
);
複製代碼

以上代碼將 HTML 中含有 .editor 類的第一個元素的內容替換爲剪貼板的內容。如果剪貼板爲空,或者不包含任何文本,則元素的內容將被清空。這是因爲在剪貼板爲空或者不包含文本時,readText 方法會返回一個空字符串。

利用 Clipboard API 我們可以很方便地操作剪貼板,但實際項目使用過程中也得考慮它的兼容性:

(圖片來源 —— caniuse.com/async-clipb…

要實現剪貼板上傳的功能,可以分爲以下 3 個步驟:

瞭解完上述步驟,接下來我們來分析一下具體實現的代碼。

6.1 前端代碼

html

<div>
   <p>請先複製圖片後再執行粘貼操作</p>
</div>
複製代碼

css

#uploadArea {
   width: 400px;
   height: 400px;
   border: 1px dashed gray;
   display: table-cell;
   vertical-align: middle;
}
#uploadArea p {
   text-align: center;
   color: #999;
}
#uploadArea img {
   max-width: 100%;
   max-height: 100%;
   display: block;
   margin: auto;
}
複製代碼

js

在以下代碼中,我們使用 addEventListener 方法爲 uploadArea 容器添加 paste 事件。在對應的事件處理函數中,我們會優先判斷當前瀏覽器是否支持異步 Clipboard API。如果支持的話,就會通過 navigator.clipboard.read 方法來讀取剪貼板中的內容。在讀取內容之後,我們會通過正則判斷剪貼板項中是否包含圖片資源,如果有的話會調用 previewImage 方法執行圖片預覽操作並把返回的 blob 對象保存起來,用於後續的上傳操作。

const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png)$/i;
const uploadAreaEle = document.querySelector("#uploadArea");

uploadAreaEle.addEventListener("paste", async (e) => {
  e.preventDefault();
  const files = [];
  if (navigator.clipboard) {
    let clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        if (IMAGE_MIME_REGEX.test(type)) {
           const blob = await clipboardItem.getType(type);
           insertImage(blob, uploadAreaEle);
           files.push(blob);
         }
       }
     }
  } else {
      const items = e.clipboardData.items;
      for (let i = 0; i < items.length; i++) {
        if (IMAGE_MIME_REGEX.test(items[i].type)) {
          let file = items[i].getAsFile();
          insertImage(file, uploadAreaEle);
          files.push(file);
        }
      }
  }
  if (files.length > 0) {
    confirm("剪貼板檢測到圖片文件,是否執行上傳操作?") 
      && upload({
           url: "/multiple",
           files,
         });
   }
});
複製代碼

若當前瀏覽器不支持異步 Clipboard API,則我們會嘗試通過 e.clipboardData.items 來訪問剪貼板中的內容。需要注意的是,在遍歷剪貼板內容項的時候,我們是通過 getAsFile 方法來獲取剪貼板的內容。當然該方法也存在兼容性問題,具體如下圖所示:

(圖片來源 —— caniuse.com/mdn-api_dat…

前面已經提到,當從剪貼板解析到圖片資源時,會讓用戶進行預覽,該功能是基於 FileReader API 來實現的,對應的代碼如下所示:

function previewImage(file, container) {
  const reader = new FileReader();
  reader.onload = function (e) {
    let img = document.createElement("img");
    img.src = e.target.result;
    container.append(img);
  };
  reader.readAsDataURL(file);
}
複製代碼

當用戶預覽完成後,如果確認上傳我們就會執行文件的上傳操作。因爲文件是從剪貼板中讀取的,所以在上傳前我們會根據文件的類型,自動爲它生成一個文件名,具體是採用時間戳加文件後綴的形式:

function upload({ url, files, fieldName = "file" }) {
  let formData = new FormData();
  files.forEach((file) => {
    let fileName = +new Date() + "." + IMAGE_MIME_REGEX.exec(file.type)[1];
    formData.append(fieldName, file, fileName);
  });
  request.post(url, formData);
}
複製代碼

前面我們已經介紹了文件上傳的多種不同場景,接下來我們來介紹一個 “特殊” 的場景 —— 大文件上傳

剪貼板上傳示例:clipboard-upload

github.com/semlinker/f…

七、大文件分塊上傳

相信你可能已經瞭解大文件上傳的解決方案,在上傳大文件時,爲了提高上傳的效率,我們一般會使用 Blob.slice 方法對大文件按照指定的大小進行切割,然後通過多線程進行分塊上傳,等所有分塊都成功上傳後,再通知服務端進行分塊合併。具體處理方案如下圖所示:

因爲在 JavaScript 中如何實現大文件併發上傳? 這篇文章中,阿寶哥已經詳細介紹了大文件併發上傳的方案,所以這裏就不展開介紹了。我們只回顧一下大文件併發上傳的完整流程:

前面我們都是介紹客戶端文件上傳的場景,其實也有服務端文件上傳的場景。比如在服務端動態生成海報後,上傳到另外一臺服務器或雲廠商的 OSS(Object Storage Service)。下面我們就以 Node.js 爲例來介紹在服務端如何上傳文件。

大文件分塊上傳示例:big-file-upload

github.com/semlinker/f…

八、服務端上傳

服務器上傳就是把文件從一臺服務器上傳到另外一臺服務器。藉助 Github 上 form-data 這個庫提供的功能,我們可以很容易地實現服務器上傳的功能。下面我們來簡單介紹一下單文件和多文件上傳的功能:

8.1 單文件上傳

const fs = require("fs");
const path = require("path");
const FormData = require("form-data");

const form1 = new FormData();
form1.append("file", fs.createReadStream(path.join(__dirname, "images/image-1.jpeg")));
form1.submit("http://localhost:3000/upload/single", (error, response) => {
  if(error) {
    console.log("單圖上傳失敗");
    return;
  }
  console.log("單圖上傳成功");
});
複製代碼

8.2 多文件上傳

const form2 = new FormData();
form2.append("file", fs.createReadStream(path.join(__dirname, "images/image-2.jpeg")));
form2.append("file", fs.createReadStream(path.join(__dirname, "images/image-3.jpeg")));
form2.submit("http://localhost:3000/upload/multiple", (error, response) => {
  if(error) {
    console.log("多圖上傳失敗");
    return;
  }
  console.log("多圖上傳成功");
});
複製代碼

觀察以上代碼可知,創建完 FormData 對象之後,我們只需要通過 fs.createReadStream API 創建可讀流,然後調用 FormData 對象的 append 方法添加表單項,最後再調用 submit 方法執行提交操作即可。

其實除了 ReadableStream 之外,FormData 對象的 append 方法還支持以下類型:

const FormData = require('form-data');
const http = require('http');

const form = new FormData();
http.request('http://nodejs.org/images/logo.png', function(response) {
  form.append('my_field', 'my value');
  form.append('my_buffer', new Buffer(10));
  form.append('my_logo', response);
});
複製代碼

服務端文件上傳的內容就介紹到這裏,關於 form-data 這個庫的其他用法,感興趣的話,可以閱讀對應的使用文檔。其實除了以上介紹的八種場景外,在日常工作中,你也可能會使用一些同步工具,比如 Syncthing 文件同步工具實現文件傳輸。好的,本文的所有內容都已經介紹完了,最後我們來做一個總結。

服務端上傳示例:server-upload

github.com/semlinker/f…

九、總結

本文阿寶哥詳細介紹了文件上傳的八種場景,希望閱讀完本文後,你對八種場景背後使用的技術有一定的瞭解。由於篇幅有限,阿寶哥就沒有展開介紹與 multipart/form-data 類型相關的內容,感興趣的小夥伴可以自行了解一下。

此外,在實際項目中,你可以考慮直接使用成熟的第三方組件,比如 Github 上的 Star 數 11K+filepond。該組件採用插件化的架構,以插件的方式,提供了非常多的功能,比如 File encodeFile renameFile posterImage previewImage crop 等。總之,它是一個很不錯的組件,以後有機會的話,大家可以嘗試一下。

十、參考資源

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://juejin.cn/post/6980142557066067982