Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 欧美视频不卡,99riav国产在线观看,久久久免费观成人影院

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          nodejs篇html批量轉(zhuǎn)pdf

          文概述:如何使用 nodejs 在服務(wù)端將 html 批量轉(zhuǎn)成 pdf 并客戶端下載。

          目標(biāo)一:使用 node 在服務(wù)端實(shí)現(xiàn) html 批量轉(zhuǎn)成 pdf


          分為兩步:

          1. 找段 html 把它轉(zhuǎn)成 pdf。
          2. 循環(huán)執(zhí)行第一步就達(dá)到了批量操作目的。
          • 準(zhǔn)備html 片段:<div>111</div>
          • 將html 片段轉(zhuǎn) pdf 。

          自己轉(zhuǎn)(存疑)?或者找個(gè)中間件。自己轉(zhuǎn)的話。。。算了,找插件。

          網(wǎng)上搜到了 html-pdf,看了下周下載量接近 7W,版本迭代 21 個(gè),最近更新時(shí)間 latest,不錯(cuò),整體滿足心里預(yù)期。

          api使用如圖:

          提供了三種接口:文件、stream 流、buffer。不同返回值類型,使用 toFile()需要 2 個(gè)參數(shù):文件地址(末尾要有文件名)、回調(diào)函數(shù)。

          toFile()結(jié)果會(huì)在指定文件夾生成 pdf 文件;

          toStream()、toBuffer()可以在回調(diào)里拿到pdf文件數(shù)據(jù)流

          目標(biāo)二:將 PDF 傳到客戶端


          怎么傳輸?

          經(jīng)過分析歸納,可以濃縮為 3 個(gè)點(diǎn):

          1. 傳輸者:誰是傳輸者?它肯定是個(gè)主動(dòng)行為,就像點(diǎn)擊事件你必須先有一個(gè)點(diǎn)擊動(dòng)作一樣。由此聯(lián)想:接口,回調(diào),node,req,res,send,end,前端 success 接收 data,res 浮現(xiàn)而出。
          2. 傳輸方法:原生 res.end(),express 框架 res.send(),res.sendFile(),res.download(),fs 模塊 res.createWriteStream(),管道 pipe()。
          3. 傳輸內(nèi)容:PDF 文件,stream 流,buffer,二進(jìn)制流,下載鏈接,base64 碼。

          以下分析可以略過直接到結(jié)果:

          分析:測(cè)試用的是 express 框架,那么可以排除原生 res.end()(存疑),為啥排除它?有 send 我干嘛還用 end;將 pdf 傳向客戶端又不是下載或?qū)懭胛募懦?createWriteStream()方法;PDF 文件可以自己跑過去嗎?肯定不行!如果啥都不動(dòng)能不能成功下載文件?也可以。鏈接下載,好像挺萬能。base64 能用嗎?。。。好像是圖片在用!不知道對(duì)文件好使不好使(存疑)。

          篩選后:

          傳輸方法:res.send()、管道 pipe();

          傳輸內(nèi)容:stream 流、buffer、下載鏈接;

          使用下載鏈接:

          經(jīng)過分析了,下載鏈接肯定能實(shí)現(xiàn)這個(gè)功能!為什么呢?只要把 pdf 的文件路徑用變量存下來,然后返回給前端,拿著絕對(duì)路徑地址模擬點(diǎn)擊下載應(yīng)該就能實(shí)現(xiàn)!但這種方法并沒有在真正意義上跟要傳輸?shù)膬?nèi)容打交道,所以還值得繼續(xù)探索。


          最終決定使用,管道pipe()發(fā)送stream流輸出PDF文件流到客戶端

          目標(biāo)三:客戶端可以成功接收并自動(dòng)下載


          網(wǎng)上搜索有:Windows.open()方法,iframe 等。感覺都有點(diǎn)偏。

          本文使用a標(biāo)簽實(shí)現(xiàn),其他方法另行嘗試。

          ajax + a標(biāo)簽

          $.ajax({
              url:'url',
              responseType:'blob',
              success (res) {
                  console.log(res.toString('utf-8'));
                  // 創(chuàng)建 blob 對(duì)象,解析流數(shù)據(jù)
              // , {
                   // 如何后端沒返回下載文件類型,則需要手動(dòng)設(shè)置:type: 'application/pdf;chartset=UTF-8' 表示下載文檔為 pdf,如果是 word 則設(shè)置為 msword,excel 為 excel
              //         type: 'application/pdf;chartset=UTF-8'
              //     }
                  const blob = new Blob([res]);
                  const a = document.createElement('a')
              //     // 兼容 webkix 瀏覽器,處理 webkit 瀏覽器中 href 自動(dòng)添加 blob 前綴,默認(rèn)在瀏覽器打開而不是下載
                  const URL = window.URL || window.webkitURL
              //     // 根據(jù)解析后的 blob 對(duì)象創(chuàng)建 URL 對(duì)象
                  const herf = URL.createObjectURL(blob)
                  // 下載鏈接
                  // a.href = 'url'
                  a.href = herf
                  // // 下載文件名,如果后端沒有返回,可以自己寫 a.download = '文件.pdf'
                  a.download = '文件.pdf'
                  // document.body.appendChild(a)
                  a.click()
                  // document.body.removeChild(a)
                  // 在內(nèi)存中移除 URL 對(duì)象
                  window.URL.revokeObjectURL(herf)
              }
          })

          傳統(tǒng)jQuery接收數(shù)據(jù)流后,需要使用new Blob([res])再處理才能繼續(xù)使用,

          而,fetch也需要,不過簡(jiǎn)便多了

          fetch+ a標(biāo)簽

          //1.請(qǐng)求
          let res = await fetch([地址]);
          //2.解析
          let data = res.blob()
          //3.創(chuàng)建a標(biāo)簽
          let eleA = document.createElement('a')
          //4.創(chuàng)建鼠標(biāo)事件對(duì)象
          let e = document.createEvent("MouseEvents")
          //5.初始化事件對(duì)象
          e.initEvent("click", false, false)
          ?
          ?
          document.body.appendChild(eleA)
          eleA.download = 'index.zip'
          eleA.href = data
          //給指定的元素,執(zhí)行事件 click 事件
          eleA.dispatchEvent(e)
          document.body.removeChild(eleA)

          報(bào)錯(cuò):

          eleA.href直接使用 blob 數(shù)據(jù)作為下載鏈接是不行的,必須使用 window.URL 對(duì)象,果然無知者無畏,更改后的代碼如下:

          //1.請(qǐng)求
          let res=await fetch([地址]);
          //2.解析
          let data=await res.blob();
          const a = document.createElement('a')
          const URL = window.URL || window.webkitURL
          const herf = URL.createObjectURL(data)
          a.href = herf
          a.download = '文件.zip'
          a.click()
          window.URL.revokeObjectURL(herf)

          使用 oFile() 測(cè)試

          server.get('/', async (req, res) => {
              toPdf(res);
          });
          ?
          ?
          function toPdf (res) {
              let archive = archiver('zip', {
              zlib: { level: 9 } // 設(shè)置壓縮級(jí)別
              });
              archive.on('error', function(err){
                  throw err;
              });
              archive.pipe(res);
              for (let i = 0; i < 100; i++) {
                  let html = `<div style="width: 100px;height:100px;background:#fff;color:red;font-size:16px;font-weight:bold">這是第 <span>${i}</span> 個(gè) pdf</div>`;
                  pdf.create(html, options).toFile(`./static/${i}.pdf`, () => {
                       archive.glob('static/*')
                       archive.finalize()
                  })
              }
          }

          看命令窗口偶爾還會(huì)報(bào) Queue Closed 錯(cuò)誤;

          觀察發(fā)現(xiàn)程序一邊轉(zhuǎn) pdf,一邊下載,而且是按照順序轉(zhuǎn)換下載有 0.1.2.3...,最后壓縮返回,這個(gè)過程循環(huán)很少時(shí)發(fā)現(xiàn)沒問題,但次數(shù)增加很多后如20,100次,當(dāng)for循環(huán)到最后一次時(shí),直接執(zhí)行archiver.finalize(),完。。。結(jié)束了,所以造成Queue Closed原因是沒限制archiver.finalize()執(zhí)行時(shí)機(jī)???

          找證據(jù):

          官方文檔顯示:

          // finalize the archive (ie we are done appending files but streams have to finish yet)

          // 完成存檔

          // 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand

          // 調(diào)用此方法后,可能會(huì)立即觸發(fā)“ close”,“ end”或“ finish”,因此請(qǐng)事先注冊(cè)

          archive.finalize();

          果然,,

          添加結(jié)束條件:

          pdf.create(html, options).toFile(`./static/${i}.pdf`, () => {
               if (i === 99) {
                 archive.glob('static/*')
                 archive.finalize()
               }
          })

          執(zhí)行成功,Queue Closed沒出現(xiàn),并且在瀏覽器自動(dòng)下載了一個(gè)壓縮包,打開后

          發(fā)現(xiàn)只有 91 個(gè)文件,缺少了最后幾個(gè)文件

          for 循環(huán)明明執(zhí)行了 100 次。百思不得解,開始我以為跟網(wǎng)絡(luò)有關(guān)系,畢竟有傳言只要(網(wǎng)絡(luò))夠快什么(隊(duì)列關(guān)閉)錯(cuò)誤都追不上你。后來我發(fā)現(xiàn)用筆記本執(zhí)行程序這個(gè)錯(cuò)誤會(huì)頻繁出現(xiàn),

          查看了 html-pdf 源碼猜測(cè)會(huì)不會(huì)是同步在阻塞,導(dǎo)致循環(huán)結(jié)束后 PDF 生成文件還未完成導(dǎo)致 Queue Closed,怎么解決呢,閉包!具體原理未知(存疑),

          經(jīng)過修改

          ((html, i) => {
            pdf.create(html).toFile(`./static/${i}.pdf`, () => {
                  if (i === 49) {
                      archive.glob('static/*')
                      archive.finalize();
                  }
              })
          })(html, i)

          成功!!!打開壓縮文件查看文件總數(shù),

          一切正常,Good!

          使用 toStream()測(cè)試

          (function(html, i) {
              pdf.create(html).toStream((err, stream) => {
                  archive.append(stream, { name: `${i}.pdf`});
                  if (i === 99) {
                      archive.finalize();
                  }
              });
          })(html, i)

          我去,第一次跑果然有問題,反復(fù)執(zhí)行多次總是缺少前幾個(gè)文件

          第一次跑,少了第一個(gè)文件 1.pdf:

          第二次,循環(huán)執(zhí)行 50 次,結(jié)果只有 45 個(gè)成功,前 5 個(gè)失敗:

          命令行打印也證明了這一點(diǎn)。。。每次失敗個(gè)數(shù)竟然還不同,但還好有個(gè)規(guī)律它們都是前幾個(gè)

          。。。

          繼續(xù)執(zhí)行,竟然還有其他報(bào)錯(cuò)類型。。。無語。。

          大致意思是:輸入資源必須是 Stream 流或者 Buffer,可能是在使用 archiver.append 時(shí)塞入了 undefined 之類的,給它上個(gè)保險(xiǎn),如果值存在才執(zhí)行

          stream && archive.append(stream, { name: `${i}.pdf`});

          綜合問題共有 2 個(gè):1.并不是所有 html 片段都進(jìn)行了轉(zhuǎn)換 PDF 的操作,可能會(huì)隨機(jī)出現(xiàn)遺漏,比如 45.46.突然就到 48;2.即使所有 html 都進(jìn)行了轉(zhuǎn)換操作,還總是缺少前面幾個(gè)文件。

          問題一解決:

          html-pdf 源碼如下圖

          研究發(fā)現(xiàn) html-pdf 的 toStream 應(yīng)該是一個(gè)異步方法,查看源碼后 stream.on('end')也證明了這一點(diǎn)。由于不是順序生成 stream 流,那么最后一個(gè)流生成并不代表所有都完成,所以當(dāng)用 i===99 判斷結(jié)束就有問題,可能會(huì)跳過某一個(gè)不執(zhí)行轉(zhuǎn)換PDF需要len=50來避免,然而添加過后,每個(gè)文件都進(jìn)行了PDF轉(zhuǎn)換,但是stream顯示為undefined,進(jìn)而PDF文件也總是少前面幾個(gè),導(dǎo)致 i=99 出現(xiàn)時(shí)還有好多 toStream 沒有完成,SO 第一個(gè)問題就出現(xiàn)了;(注:不過異步操作也因此避免同步帶來方法一的問題:隊(duì)列關(guān)閉錯(cuò)誤。)

          原因找到,解決辦法就是添加變量手動(dòng)強(qiáng)行控制進(jìn)程:

          如圖,len 初始 100,減為 0 時(shí)代表所有都已經(jīng)轉(zhuǎn)換,可以結(jié)束

          let len = 100;
          然后,開始計(jì)數(shù),不到最后一個(gè)完成 stream 流轉(zhuǎn)換不結(jié)束
          pdf.create(html).toStream((err, stream) => {
              len--;
              stream && archive.append(stream, { name: `${i}.pdf`});
              if (len === 0) {
                  archive.finalize();
              }
          });

          命令行打印下錯(cuò)誤信息看看怎么回事控制臺(tái)

          問題二解決:

          先打印下錯(cuò)誤信息 console.log('err:', err)


          暴露了,PDF generation timeout. 一個(gè) timeout 已經(jīng)夠了,能夠說明很多問題,html-pdf 提供了轉(zhuǎn)換超時(shí)限制,時(shí)間超出 timeout 自然無法成功轉(zhuǎn)換成 stream 流輸出,解決辦法更簡(jiǎn)單:

          官方文檔,給出了一個(gè)配置項(xiàng) options:{}對(duì)象,其中就有 timeout 設(shè)置,我們可以視情況放大此參數(shù),

          // 一分鐘內(nèi)轉(zhuǎn) pdf 不成功,則視為失敗
          let options = {
              timeout: 60000
          }
          pdf.create(html, options).toStream((err, stream) => {
              。。。。。。
          });

          繼續(xù)測(cè)試,終于完成。。。

          結(jié)語:

          1.本文走了許多彎路,踩了多多個(gè)坑;

          2.文中標(biāo)記存疑的地方依然有很多,都是等待去學(xué)習(xí)理解的地方;

          3.對(duì)于名詞、問題的解釋描述不夠精準(zhǔn)透徹,需要深度挖掘?qū)χR(shí)點(diǎn)的認(rèn)知;

          4.對(duì)問題的解決方式不夠標(biāo)準(zhǔn)、熟練,這才是造成多走彎路的原因;

          5.雖然解決問題才是我們的最終目的,但是仍需追求解決方式的多樣化,找到問題的根源 格物致知 才能給自己醍醐灌頂之感;

          文轉(zhuǎn)載自微信公眾號(hào)「全棧修仙之路」,作者阿寶哥。轉(zhuǎn)載本文請(qǐng)聯(lián)系全棧修仙之路公眾號(hào)。

          在日常工作中,文件上傳是一個(gè)很常見的功能。在上傳文件時(shí),我們可以選擇上傳單個(gè)文件,也可以通過設(shè)置 multiple 屬性來上傳多個(gè)文件。

          本文阿寶哥將介紹如何上傳目錄及如何壓縮目錄并上傳,壓縮目錄的功能是通過 JSZip 這個(gè)庫來實(shí)現(xiàn)。利用這個(gè)庫還可以實(shí)現(xiàn)在線預(yù)覽 ZIP 文件的功能,感興趣的小伙伴可以閱讀 JavaScript 如何在線解壓 ZIP 文件? 這篇文章。下面我們先來介紹如何實(shí)現(xiàn)壓縮目錄并上傳的功能。

          一、瀏覽器端

          1.1 選擇目錄

          在瀏覽器端,要實(shí)現(xiàn)壓縮目錄并上傳的功能。首先我們要先實(shí)現(xiàn)選擇目錄的功能,要實(shí)現(xiàn)該功能,我們可以直接使用 HTMLInputElement 元素的 webkitdirectory 屬性:

          <input type="file" id="uploadFile" webkitdirectory /> 

          當(dāng)設(shè)置了 webkitdirectory 屬性之后,我們就可以選擇目錄了。當(dāng)阿寶哥選擇了 useAxios 目錄之后,就會(huì)顯示以下確認(rèn)框:

          點(diǎn)擊上傳按鈕之后,我們就可以獲取文件列表。列表中的文件對(duì)象上含有一個(gè) webkitRelativePath 屬性,用于表示當(dāng)前文件的相對(duì)路徑。在進(jìn)行目錄壓縮的時(shí)候,我們就會(huì)使用到該屬性。

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

          (圖片來源 —— https://caniuse.com/?search=webkitdirectory)

          1.2 壓縮目錄

          在 JavaScript 如何在線解壓 ZIP 文件? 這篇文章中,阿寶哥介紹了在瀏覽器端如何使用 JSZip 這個(gè)庫實(shí)現(xiàn)在線解壓 ZIP 文件的功能。JSZip 這個(gè)庫除了可以解析 ZIP 文件之外,它還可以用來創(chuàng)建和編輯 ZIP 文件。這里阿寶哥基于 JSZip 庫提供的 API,封裝了一個(gè) generateZipFile 函數(shù):

          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) { // 生成zip文件 
                zipName = zipName || Date.now() + ".zip"; 
                const zipFile = new File([blob], zipName, { 
                  type: "application/zip", 
                }); 
                resolve(zipFile); 
              }); 
            }); 
          } 

          在以上代碼中,我們使用 file(name, data [,options]) 方法,把目錄中的文件依次添加到 zip 對(duì)象中,然后再通過 generateAsync 方法來生成 ZIP 文件。在生成 ZIP 文件時(shí),我們可以設(shè)置該文件的類型。這里我們?cè)O(shè)置的默認(rèn)類型為 blob 類型,除了支持 blob 類型之外,它還支持 base64、uint8array 和 arraybuffer 等類型。

          1.3 上傳壓縮 ZIP 文件

          在壓縮目錄生成 ZIP 文件之后,我們就可以通過 XMLHttpRequest 或 fetch API 來上傳壓縮文件。下面阿寶哥將以 axios 為例,來實(shí)現(xiàn)文件上傳的功能。

          html 代碼

          <input type="file" id="uploadFile" webkitdirectory /> 
          <button id="submit" onclick="uploadFile()">上傳文件</button> 

          js 代碼

          const uploadFileEle = document.querySelector("#uploadFile"); 
          const uploadOptions = { needZip = true }; 
           
          const request = axios.create({ 
            baseURL: "http://localhost:3000/", 
            timeout: 5000, 
          }); 
           
          async function uploadFile({ needZip } = uploadOptions) { 
            if (!uploadFileEle.files.length) return; 
            let fileList = uploadFileEle.files; 
            if (needZip) { // 對(duì)目錄進(jìn)行ZIP壓縮 
              let webkitRelativePath = fileList[0].webkitRelativePath; 
              let zipFileName = webkitRelativePath.split("/")[0] + ".zip"; 
              fileList = [await generateZipFile(zipFileName, fileList)]; 
            } 
            uploadFiles({ // 上傳文件列表 
              url: "/upload/multiple", 
              files: fileList, 
            }); 
          } 

          在 uploadFile 函數(shù)中,如果有啟用目錄壓縮功能,我們就會(huì)調(diào)用 generateZipFile 函數(shù)生成 ZIP 文件,如果沒有的話,就會(huì)直接調(diào)用 uploadFiles 函數(shù)來上傳目錄中的所有文件,當(dāng)然你也可以對(duì)文件列表進(jìn)行過濾,比如限制文件類型或文件的大小等。

          下面我們來看一下 uploadFiles 函數(shù)的具體實(shí)現(xiàn):

          function uploadFiles({ url, files, fieldName = "file" }) { 
            if (!url || !files.length) return; 
            let formData = new FormData(); 
            for (let i = 0; i < files.length; i++) { 
              formData.append(fieldName, files[i], files[i].name); 
            } 
            return request.post(url, formData); 
          } 

          在 uploadFiles 函數(shù)中,我們通過創(chuàng)建 FormData 對(duì)象來保存文件的信息,然后通過 request(axios 實(shí)例)來執(zhí)行上傳操作。

          二、服務(wù)器端

          2.1 接收 ZIP 文件

          在服務(wù)端要實(shí)現(xiàn)文件上傳功能也比較簡(jiǎn)單,這里阿寶哥以 koa 為例來實(shí)現(xiàn)文件上傳的功能。如果你對(duì) koa 還不了解的話,建議你先大致瀏覽一下 koa 的官方文檔。

          const path = require("path"); 
          const Koa = require("koa"); 
          const cors = require("@koa/cors"); 
          const multer = require("@koa/multer"); 
          const Router = require("@koa/router"); 
           
          const app = new Koa(); 
          const router = new Router(); 
          const UPLOAD_DIR = path.join(__dirname, "/public/upload"); 
           
          const storage = multer.diskStorage({ 
            destination: async function (req, file, cb) { // 設(shè)置文件的存儲(chǔ)目錄 
              cb(null, UPLOAD_DIR); 
            }, 
            filename: function (req, file, cb) { // 設(shè)置文件名 
              cb(null, `${file.originalname}`); 
            }, 
          }); 
           
          const multerUpload = multer({ storage }); 
           
          router.get("/", async (ctx) => { 
            ctx.body = "壓縮文件目錄上傳示例(阿寶哥)"; 
          }); 
           
          router.post( 
            "/upload/multiple", 
            multerUpload.fields([ 
              { 
                name: "file", 
              }, 
            ]), 
            async (ctx, next) => { 
              ctx.body = { 
                status: "success", 
                msg: "文件上傳成功", 
              }; 
            } 
          ); 
           
          // 注冊(cè)中間件 
          app.use(cors()); 
          app.use(router.routes()).use(router.allowedMethods()); 
           
          app.listen(3000, () => { 
            console.log("app starting at port 3000"); 
          }); 

          在以上代碼中,我們通過 @koa/multer 這個(gè)中間件來處理文件上傳,對(duì)該中間件感興趣的小伙伴,可以自行閱讀官方文檔。接下來,我們來繼續(xù)討論另一個(gè)問題 —— 如何接收目錄并按照文件目錄結(jié)構(gòu)進(jìn)行存放?

          2.2 接收文件目錄

          前面我們已經(jīng)知道,當(dāng) input[type="file"] 使用了 webkitdirectory 屬性之后,返回 File 對(duì)象的 webkitRelativePath 屬性就會(huì)存放當(dāng)前文件相對(duì)于當(dāng)前目錄的相對(duì)路徑:

          因此當(dāng)我們?cè)诜?wù)端處理文件目錄上傳的功能時(shí),我們就可以通過該屬性來創(chuàng)建對(duì)應(yīng)的目錄結(jié)構(gòu),具體的處理邏輯如下所示:

          const fse = require("fs-extra"); 
           
          const storage = multer.diskStorage({ 
            destination: async function (req, file, cb) { 
              // 把useAxios@demo.vue中的@替換為路徑分隔符 
              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); // 確保當(dāng)前目錄存在 
              cb(null, fileDir); 
            }, 
            filename: function (req, file, cb) { 
              let parts = file.originalname.split("@"); // 對(duì)路徑進(jìn)行拆分 
              cb(null, `${parts[parts.length - 1]}`); // 獲取文件名 
            }, 
          }); 

          為什么 originalname 文件原始名稱會(huì)包含 @ 符號(hào)呢?這樣因?yàn)槭褂?useAxios/demo.vue 這種路徑形式時(shí),是不能獲取到完整的路徑名稱,只能獲取到文件名。為了解決這個(gè)問題,阿寶哥在上傳文件時(shí),手動(dòng)把文件相對(duì)路徑中的 / 符號(hào)替換為 @ 然后再進(jìn)行上傳,對(duì)應(yīng)的處理邏輯如下:

          function uploadFiles({ url, files, fieldName = "file" }) { 
            if (!url || !files.length) return; 
            let formData = new FormData(); 
            for (let i = 0; i < files.length; i++) { 
              formData.append(fieldName, files[i], files[i].webkitRelativePath.replace(/\//g, "@")); 
            } 
            return request.post(url, formData); 
          } 

          好的,壓縮目錄上傳和目錄上傳已經(jīng)介紹完了,感興趣的小伙伴可以動(dòng)手試試看。由于完整的示例代碼內(nèi)容比較多,阿寶哥就不放具體的代碼了。有需要的小伙伴,可以訪問以下地址瀏覽示例代碼。

          https://gist.github.com/semlinker/af57349c16d203cc2ec845d4b5a6b445

          注意:以上代碼僅供參考,請(qǐng)根據(jù)實(shí)際業(yè)務(wù)進(jìn)行調(diào)整。

          三、總結(jié)

          本文阿寶哥介紹了如何利用 input[type="file"] 元素的 webkitdirectory 屬性來實(shí)現(xiàn)選擇目錄的功能,然后利用 JSZip 這個(gè)庫來實(shí)現(xiàn)目錄壓縮,最后通過 axios 來上傳目錄壓縮后的 ZIP 文件 。此外,阿寶哥還介紹了如何使用 koa 來實(shí)現(xiàn)接收目錄并按照文件目錄結(jié)構(gòu)進(jìn)行存放的功能。

          四、參考資源

          JSZip 官方文檔

          MDN - webkitdirectory

          JavaScript 如何在線解壓 ZIP 文件?


          主站蜘蛛池模板: 国产精品乱码一区二区三| 亚洲Av永久无码精品一区二区| 国模极品一区二区三区| 国产成人精品第一区二区| 亚洲午夜在线一区| 国产一区二区三区精品久久呦| 一区二区传媒有限公司| 国产福利日本一区二区三区| 色一情一乱一伦一区二区三欧美| 丝袜人妻一区二区三区网站| 精品久久久久久中文字幕一区| 狠狠做深爱婷婷久久综合一区| 性色av闺蜜一区二区三区| 波多野结衣电影区一区二区三区| 中文字幕日本一区| 一区二区三区杨幂在线观看| 一夲道无码人妻精品一区二区| 蜜桃臀无码内射一区二区三区| 亚洲人成网站18禁止一区| 午夜无码视频一区二区三区| 91大神在线精品视频一区| 国产成人精品一区二区三区无码| 国产在线观看一区二区三区四区 | 亚洲Av无码一区二区二三区| 一区二区三区免费高清视频| 丰满爆乳无码一区二区三区| 亚洲av色香蕉一区二区三区 | 国产成人一区二区在线不卡 | 中文字幕av人妻少妇一区二区| 福利电影一区二区| 国产精品丝袜一区二区三区 | 精品无码综合一区| 亚洲A∨精品一区二区三区| 国产成人亚洲综合一区| 人妻体体内射精一区二区| 无码av不卡一区二区三区| 国产精品一区二区久久精品涩爱| 武侠古典一区二区三区中文| 亚洲国产视频一区| 人妻夜夜爽天天爽爽一区| 国产美女口爆吞精一区二区|