整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端上傳大文件怎么處理(遲早用得到,抓緊收藏!)

          前端上傳大文件怎么處理(遲早用得到,抓緊收藏!)

          • 屬于專欄-項目難點
          • 重要程度:★★★★★

          背景

          當我們在做文件的導入功能的時候,如果導入的文件過大,可能會導所需要的時間夠長,且失敗后需要重新上傳,我們需要前后端結合的方式解決這個問題

          思路

          我們需要做幾件事情如下:

          • 對文件做切片,即將一個請求拆分成多個請求,每個請求的時間就會縮短,且如果某個請求失敗,只需要重新發送這一次請求即可,無需從頭開始
          • 通知服務器合并切片,在上傳完切片后,前端通知服務器做合并切片操作
          • 控制多個請求的并發量,防止多個請求同時發送,造成瀏覽器內存溢出,導致頁面卡死
          • 做斷點續傳,當多個請求中有請求發送失敗,例如出現網絡故障、頁面關閉等,我們得對失敗的請求做處理,讓它們重復發送

          實現

          前端

          示例代碼倉庫

          倉庫地址

          步驟1-切片,合并切片

          在JavaScript中,文件FIle對象是Blob對象的子類,Blob對象包含一個重要的方法slice通過這個方法,我們就可以對二進制文件進行拆分,具體代碼如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=s, initial-scale=1.0">
              <title>Document</title>
              <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
          </head>
          <body>
              <input type="file" id="fileInput">
              <button id="uploadBtn">上傳</button>
          </body>
          <script>
          // 請求基準地址
          axios.defaults.baseURL='http://localhost:3000'
          // 選中的文件
          var file=null
          // 選擇文件
          document.getElementById('fileInput').onchange=function({target: {files}}){
              file=files[0] 
          }
          // 開始上傳
          document.getElementById('uploadBtn').onclick=async function(){
              if (!file) return
              // 創建切片   
              // let size=1024 * 1024 * 10 //10MB 切片大小
              let size=1024 * 50  //50KB 切片大小
              let fileChunks=[]
              let index=0 //切片序號
              for(let cur=0; cur < file.size; cur +=size){
                  fileChunks.push({
                      hash: index++,
                      chunk: file.slice(cur, cur + size)
                  })
              }
              // 上傳切片
              const uploadList=fileChunks.map((item, index)=> {
                  let formData=new FormData()
                  formData.append('filename', file.name)
                  formData.append('hash', item.hash)
                  formData.append('chunk', item.chunk)
                  return axios({
                      method: 'post',
                      url: '/upload',
                      data: formData
                  })
              })
              await Promise.all(uploadList)
              // 合并切片
              await axios({
                  method: 'get',
                  url: '/merge',
                  params: {
                      filename: file.name
                  }
              });
              console.log('上傳完成')
          }
          </script>
          </html>
          

          步驟2-并發控制

          結合Promise.race異步函數實現,多個請求同時并發的數量,防止瀏覽器內存溢出,具體代碼如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=s, initial-scale=1.0">
              <title>Document</title>
              <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
          </head>
          <body>
              <input type="file" id="fileInput">
              <button id="uploadBtn">上傳</button>
          </body>
          <script>
          // 請求基準地址
          axios.defaults.baseURL='http://localhost:3000'
          // 選中的文件
          var file=null
          // 選擇文件
          document.getElementById('fileInput').onchange=function({target: {files}}){
              file=files[0] 
          }
          // 開始上傳
          document.getElementById('uploadBtn').onclick=async function(){
              if (!file) return
              // 創建切片   
              // let size=1024 * 1024 * 10; //10MB 切片大小
              let size=1024 * 50 //50KB 切片大小
              let fileChunks=[]
              let index=0 //切片序號
              for(let cur=0; cur < file.size; cur +=size){
                  fileChunks.push({
                      hash: index++,
                      chunk: file.slice(cur, cur + size)
                  });
              }
              // 控制并發
              let pool=[]//并發池
              let max=3 //最大并發量
              for(let i=0;i<fileChunks.length;i++){
                  let item=fileChunks[i]
                  let formData=new FormData()
                  formData.append('filename', file.name)
                  formData.append('hash', item.hash)
                  formData.append('chunk', item.chunk)
                  // 上傳切片
                  let task=axios({
                      method: 'post',
                      url: '/upload',
                      data: formData
                  })
                  task.then((data)=>{
                      //請求結束后將該Promise任務從并發池中移除
                      let index=pool.findIndex(t=> t===task)
                      pool.splice(index)
                  })
                  pool.push(task)
                  if(pool.length===max){
                      //每當并發池跑完一個任務,就再塞入一個任務
                      await Promise.race(pool)
                  }
              }
              //所有任務完成,合并切片
              await axios({
                  method: 'get',
                  url: '/merge',
                  params: {
                      filename: file.name
                  }
              });
              console.log('上傳完成')
          }
          </script>
          </html>
          

          步驟3-斷點續傳

          在單個請求失敗后,觸發catch的方法的時候,講當前請求放到失敗列表中,在本輪請求完成后,重復對失敗請求做處理,具體代碼如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=s, initial-scale=1.0">
              <title>Document</title>
              <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
          </head>
          <body>
              <input type="file" id="fileInput">
              <button id="uploadBtn">上傳</button>
          </body>
          <script>
          // 請求基準地址
          axios.defaults.baseURL='http://localhost:3000'
          // 選中的文件
          var file=null
          // 選擇文件
          document.getElementById('fileInput').onchange=function({target: {files}}){
              file=files[0] 
          }
          // 開始上傳
          document.getElementById('uploadBtn').onclick=function(){
              if (!file) return;
              // 創建切片   
              // let size=1024 * 1024 * 10; //10MB 切片大小
              let size=1024 * 50; //50KB 切片大小
              let fileChunks=[];
              let index=0 //切片序號
              for(let cur=0; cur < file.size; cur +=size){
                  fileChunks.push({
                      hash: index++,
                      chunk: file.slice(cur, cur + size)
                  })
              }
              // 控制并發和斷點續傳
              const uploadFileChunks=async function(list){
                  if(list.length===0){
                      //所有任務完成,合并切片
                      await axios({
                          method: 'get',
                          url: '/merge',
                          params: {
                              filename: file.name
                          }
                      });
                      console.log('上傳完成')
                      return
                  }
                  let pool=[]//并發池
                  let max=3 //最大并發量
                  let finish=0//完成的數量
                  let failList=[]//失敗的列表
                  for(let i=0;i<list.length;i++){
                      let item=list[i]
                      let formData=new FormData()
                      formData.append('filename', file.name)
                      formData.append('hash', item.hash)
                      formData.append('chunk', item.chunk)
                      // 上傳切片
                      let task=axios({
                          method: 'post',
                          url: '/upload',
                          data: formData
                      })
                      task.then((data)=>{
                          //請求結束后將該Promise任務從并發池中移除
                          let index=pool.findIndex(t=> t===task)
                          pool.splice(index)
                      }).catch(()=>{
                          failList.push(item)
                      }).finally(()=>{
                          finish++
                          //所有請求都請求完成
                          if(finish===list.length){
                              uploadFileChunks(failList)
                          }
                      })
                      pool.push(task)
                      if(pool.length===max){
                          //每當并發池跑完一個任務,就再塞入一個任務
                          await Promise.race(pool)
                      }
                  }
              }
              uploadFileChunks(fileChunks)
          
          }
          </script>
          </html>
          

          后端

          步驟1.安裝依賴

          npm i express@4.17.2
          npm i multiparty@4.2.2
          

          步驟2.接口實現

          const express=require('express')
          const multiparty=require('multiparty')
          const fs=require('fs')
          const path=require('path')
          const { Buffer }=require('buffer')
          // 上傳文件最終路徑
          const STATIC_FILES=path.join(__dirname, './static/files')
          // 上傳文件臨時路徑
          const STATIC_TEMPORARY=path.join(__dirname, './static/temporary')
          const server=express()
          // 靜態文件托管
          server.use(express.static(path.join(__dirname, './dist')))
          // 切片上傳的接口
          server.post('/upload', (req, res)=> {
              const form=new multiparty.Form();
              form.parse(req, function(err, fields, files) {
                  let filename=fields.filename[0]
                  let hash=fields.hash[0]
                  let chunk=files.chunk[0]
                  let dir=`${STATIC_TEMPORARY}/${filename}`
                  // console.log(filename, hash, chunk)
                  try {
                      if (!fs.existsSync(dir)) fs.mkdirSync(dir)
                      const buffer=fs.readFileSync(chunk.path)
                      const ws=fs.createWriteStream(`${dir}/${hash}`)
                      ws.write(buffer)
                      ws.close()
                      res.send(`${filename}-${hash} 切片上傳成功`)
                  } catch (error) {
                      console.error(error)
                      res.status(500).send(`${filename}-${hash} 切片上傳失敗`)
                  }
              })
          })
          //合并切片接口
          server.get('/merge', async (req, res)=> {
              const { filename }=req.query
              try {
                  let len=0
                  const bufferList=fs.readdirSync(`${STATIC_TEMPORARY}/${filename}`).map((hash,index)=> {
                      const buffer=fs.readFileSync(`${STATIC_TEMPORARY}/${filename}/${index}`)
                      len +=buffer.length
                      return buffer
                  });
                  //合并文件
                  const buffer=Buffer.concat(bufferList, len);
                  const ws=fs.createWriteStream(`${STATIC_FILES}/${filename}`)
                  ws.write(buffer);
                  ws.close();
                  res.send(`切片合并完成`);
              } catch (error) {
                  console.error(error);
              }
          })
          
          server.listen(3000, _=> {
              console.log('http://localhost:3000/')
          })
          

          其他實現

          如果使用騰訊云阿里云文件上傳的服務,它們提供了npm庫,例如騰訊云的cos-js-sdk-v5,它自身提供的切片相關的配置

          SS加載確實有可能阻塞頁面加載,但這并非絕對,具體取決于CSS的加載方式、應用位置以及瀏覽器的渲染機制。在了解CSS加載如何影響頁面加載之前,我們先要明白瀏覽器渲染頁面的基本流程。

          瀏覽器在加載網頁時,會按照從上到下的順序解析HTML文檔。當瀏覽器遇到`<link>`標簽引用外部CSS文件時,它會停止HTML的解析,轉而加載并應用這個CSS文件。這個過程被稱為CSS阻塞。因此,如果這個CSS文件很大或者加載速度很慢,用戶可能會看到一個空白頁面,直到CSS文件完全加載并應用。

          然而,有幾種方法可以避免或減輕CSS加載對頁面加載的阻塞:

          1. 異步加載CSS:通過將CSS文件的加載設置為異步,可以確保HTML解析不會被阻塞。這可以通過在`<link>`標簽中添加`rel="async"`屬性來實現。這樣,瀏覽器會在后臺加載CSS文件,而不會停止HTML的解析。
          2. 內聯CSS:將CSS代碼直接寫在HTML文件中,而不是通過外部文件引用,可以避免網絡請求造成的延遲。但是,這會增加HTML文件的大小,可能導致其他性能問題。
          3. 使用CSS-in-JS庫:一些庫,如Styled Components或Emotion,允許你在JavaScript中編寫CSS。這種方法可以動態生成樣式,但也可能增加JavaScript的復雜性。
          4. 分割CSS:將CSS文件分割成多個小文件,每個文件只包含一部分樣式。這樣,即使某個文件加載較慢,也不會阻塞整個頁面的渲染。
          5. 利用媒體查詢:通過媒體查詢,你可以根據設備的特性(如屏幕大小、分辨率等)加載不同的CSS文件。這樣,用戶只會下載并應用他們真正需要的樣式。
          6. 預加載和預獲取:使用`<link rel="preload">`和`<link rel="prefetch">`可以告訴瀏覽器提前加載CSS文件。雖然這并不能阻止CSS阻塞,但它可以確保文件在需要時立即可用。

          此外,值得注意的是,現代瀏覽器通常具有一些優化機制,如并行下載、緩存等,這些都可以幫助減少CSS加載對頁面加載的影響。

          總的來說,CSS加載確實有可能阻塞頁面加載,但通過一些優化策略和技術,我們可以減輕或避免這種阻塞。選擇哪種策略取決于你的具體需求和約束。

          **前端實現大文件上傳**

          **引言:**

          隨著互聯網技術的發展,用戶在線處理大量數據的需求日益增強,其中涉及大文件上傳的功能已成為許多Web應用不可或缺的一部分。然而,傳統表單提交往往受限于瀏覽器的限制和服務器處理能力,無法很好地滿足大文件高效穩定上傳的需求。本文將深入探討前端實現大文件上傳的關鍵技術和策略,輔以實際HTML+JS代碼示例,助您構建高性能、用戶友好的文件上傳體驗。

          ## **一、理解瀏覽器上傳限制**

          **1.1 瀏覽器最大請求大小限制**

          大多數現代瀏覽器默認允許的最大HTTP POST請求大小約為2GB到4GB不等,但具體值會受到服務器配置的影響。因此,在實現大文件上傳之前,需要確保服務器端的接收限制足夠高。

          **1.2 超時問題**

          大文件上傳過程中,網絡狀況不佳或文件過大可能導致請求超時。對此,可通過設置合理的超時重試機制,以及使用分片上傳來解決。

          ## **二、分片上傳與斷點續傳**

          **2.1 分片上傳概念**

          分片上傳是將大文件分割成多個小塊,獨立上傳每一塊,最后在服務器端重組的方式。這樣可以有效避免一次性上傳大文件可能引發的問題。

          ```html

          <!-- HTML 文件選擇器 -->

          <input type="file" id="fileInput" accept=".zip,.rar">

          <script>

          document.getElementById('fileInput').addEventListener('change', function(e) {

          const file=e.target.files[0];

          // 假設每個分片大小為1MB

          const chunkSize=1 * 1024 * 1024;

          // 計算分片數量

          const chunks=Math.ceil(file.size / chunkSize);

          for (let i=0; i < chunks; i++) {

          const start=i * chunkSize;

          const end=Math.min(start + chunkSize, file.size);


          // 創建File Slice

          const chunk=file.slice(start, end);

          // 發起異步上傳請求

          uploadChunk(chunk, i, chunks);

          }

          });

          function uploadChunk(chunk, index, total) {

          // 這里僅展示發起上傳請求的邏輯,實際需要包含chunk索引和總數量等信息

          const xhr=new XMLHttpRequest();

          xhr.open('POST', '/api/upload/chunk', true);

          xhr.setRequestHeader('Content-Type', 'application/octet-stream');

          xhr.onload=()=> {

          if (xhr.status===200) {

          // 上傳成功處理邏輯

          } else {

          // 處理錯誤或重試

          }

          };

          xhr.onerror=()=> {

          // 錯誤處理

          };

          xhr.send(chunk);

          }

          </script>

          ```

          **2.2 斷點續傳**

          斷點續傳是在分片上傳的基礎上,記錄已上傳成功的分片信息,如果上傳過程因網絡問題中斷,可以從上次失敗的地方繼續上傳。這通常需要在客戶端存儲上傳進度信息,并在下次上傳時發送給服務器校驗。

          ```javascript

          // 假設有本地持久化存儲已上傳分片信息的方法

          function saveUploadProgress(progressData) {

          localStorage.setItem('uploadProgress', JSON.stringify(progressData));

          }

          // 加載已上傳的分片信息

          function loadUploadProgress() {

          const progressData=localStorage.getItem('uploadProgress');

          return progressData ? JSON.parse(progressData) : null;

          }

          // 在初始化上傳階段檢查并恢復未完成的上傳任務

          const previousProgress=loadUploadProgress();

          if (previousProgress) {

          for (const {index, chunk} of previousProgress.unfinishedChunks) {

          // 繼續上傳未完成的分片

          uploadChunk(chunk, index, previousProgress.totalChunks);

          }

          }

          ```

          ## **三、前端上傳組件與庫推薦**

          **3.1 React Dropzone Uploader**

          React Dropzone Uploader是一個基于React的組件庫,內置了分片上傳和斷點續傳功能,可輕松集成至您的React項目中。

          **3.2 Resumable.js**

          Resumable.js 是一個輕量級、跨瀏覽器的大文件上傳庫,它支持分片上傳、斷點續傳及自定義事件通知等功能。

          ## **四、實時進度顯示與用戶體驗優化**

          **4.1 實現上傳進度條**

          在每個分片上傳完成后更新進度條,讓用戶體驗更加直觀。

          ```javascript

          xhr.upload.onprogress=function(event) {

          if (event.lengthComputable) {

          const percentComplete=event.loaded / event.total;

          updateProgressBar(percentComplete);

          }

          };

          function updateProgressBar(percentage) {

          // 更新頁面上的進度條UI

          }

          ```

          **4.2 錯誤處理與提示**

          對于上傳過程中可能出現的各類錯誤,如網絡中斷、服務器異常等,都需要提供清晰且友好的錯誤提示,并賦予用戶重新上傳或恢復上傳的能力。

          總結:

          前端實現大文件上傳不僅涉及到技術層面的挑戰,還要求關注用戶體驗的設計。通過合理利用分片上傳、斷點續傳等技術,結合優秀的前端組件或庫,我們可以打造出穩定可靠、易用性高的大文件上傳功能,從而提升產品的綜合競爭力。同時,針對不同的業務場景,還需考慮文件安全性、并發控制、隊列管理等問題,確保整個上傳流程的健壯性。


          主站蜘蛛池模板: 一区在线观看视频| 亚洲日本va一区二区三区| 精品无码成人片一区二区| 亚洲色无码专区一区| 国内精品视频一区二区三区 | 国内精品一区二区三区在线观看| 视频在线观看一区| 午夜精品一区二区三区免费视频| 中文字幕一区在线观看视频| 香蕉久久AⅤ一区二区三区 | 精品久久久久久中文字幕一区| 中文字幕一区二区三区四区 | 91video国产一区| 亚洲福利视频一区二区| 一区二区三区在线观看| 91一区二区三区| 国产精品综合一区二区| 亚洲AV无码一区二区三区人| 国产在线aaa片一区二区99| 尤物精品视频一区二区三区| 国产经典一区二区三区蜜芽 | 国产色综合一区二区三区| 无码AV动漫精品一区二区免费| 91久久精品一区二区| 无码乱人伦一区二区亚洲| 中文字幕久久亚洲一区| 亚洲av片一区二区三区| 人妻内射一区二区在线视频| 日本夜爽爽一区二区三区| 中文字幕一区在线观看| 好看的电影网站亚洲一区| 国产一区二区福利| 国产丝袜视频一区二区三区| 国产精品高清一区二区三区| 亚洲av福利无码无一区二区| 亚洲欧美日韩一区二区三区在线| 久久精品国内一区二区三区| 亚洲国产专区一区| 国产精品高清一区二区人妖| 国产成人欧美一区二区三区| 中文字幕在线视频一区|