天天看點

ajax上傳檔案顯示進度

下面要做一個ajax上傳檔案顯示進度的操作: 

ajax上傳檔案顯示進度
這裡先上代碼:

1、前端代碼

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet">
  <style>
    .progress-area {
      padding: 20px;
    }
  </style>
</head>
<body>
  <div class="">
    <p>檔案上傳</p>
    <div>
      <input type="file" name="file" id="file" />
      <div class="img-preview">
          <p>圖檔預覽(如果上傳檔案時圖檔)</p>
          <img id="image" src="" width="200" />
      </div>
      <button id="upload">上傳</button>
      <button id="btn">終止上傳</button>
      <div class="progress-area">
        進度
        <div class="progress">
          <div class="progress-bar" id="progress" role="progressbar"  aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">0%</div>
        </div>
      </div>
    </div>
  </div>
  <script>
    (function () {
        'use strict';
        var file = document.querySelector('#file');
        var upload = document.querySelector('#upload');
        var progress = document.querySelector('#progress');
        var image = document.querySelector('#image');
        var xhr = new XMLHttpRequest();

        var loaded = 0;
        var total = 0;

        upload.addEventListener('click', uploadFile, false);
        file.addEventListener('change', previewImage, false);

        // 點選上傳
        function uploadFile(event) {
          if(!file.files[0]) {
            alert('請選擇檔案')
            return
          }
          var formData = new FormData(); 
          // 利用這個來送出xhr,具體的可看下面的mdn 介紹
           // https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects
          formData.append('test-upload', file.files[0]); 
          // 将input 選擇出來的檔案調用append()方法來添加字段
          xhr.onload = uploadSuccess; 
          // 上傳方法
          xhr.upload.onprogress = setProgress;
          // 上傳過程方法
          xhr.open('post', '/upload', true);
          // 打開請求方式和接口
          xhr.send(formData);
          // 發送資料
        }

        // 成功上傳
        function uploadSuccess(event) {
          if (xhr.readyState === 4 && xhr.status === 200) { // 判斷接口調用成功的重要辨別
            setTimeout(()=> {
              alert('上傳成功')
              // window.location.href="/"
            },1000)
          }
        }

        // 進度條
        function setProgress(event) {
          console.log(event)
          if (event.lengthComputable) { 
          // 先判斷這個上傳程序是否有這個檔案長度的判斷
            loaded = event.loaded 
            // 已經加載的位元組
            total = event.total 
            // 上傳伊始時候計算的總位元組數
            var complete = Number.parseInt(event.loaded / event.total * 100);
            // 取百分比
            progress.style.width = complete + '%';
            progress.innerHTML = complete + '%';
            // css 偷了懶,用了bootstrap4的css
          }
        }

        // 圖檔預覽
        function previewImage(event) {
          var reader = new FileReader(); 
          // h5 的新屬性 
          reader.onload = function (event) {
            image.src = event.target.result; 
            // 這裡是用圖檔base64的字元串将圖檔顯示出來,用作圖檔上傳預覽
          };
          reader.readAsDataURL(event.target.files[0]);
        }

        //++++++++++++++++++++++++++++++++++++++++++++
        xhr.onloadstart = function(){
          console.log("上傳開始");    
        }
        btn.onclick = function(){
          xhr.abort();
          console.log("上傳被終止.");
          progress.style.width = 0 + '%';
          progress.innerHTML = 0 + '%';
          // 這裡調用取消上傳的代碼
        };
        xhr.ontimeout = function(){
          console.log('上傳逾時.');
        }
        // xhr.timeout = 50000; // 預設為0 沒有時間限制
        // xhr.onabort = function(){
        //   console.log("The transfer has been canceled by the user.");
        // }
        xhr.onerror = function(){
          console.log("上傳錯誤,可能是斷網了,也可能是斷請求服務了."); 
          // 這裡存在異步傳輸問題
          return
        }
        xhr.onloadend = function(){
          console.log("請求結束"); 
          // 發送上傳的請求,至于有沒有上傳成功,不清楚,可能失敗 成功,這裡隻是請求結束了 
        }
        // +++++++++++++++++++++++++++++++++++++++++++
      })();
  </script>
</body>
</html>      

2、後端接口(nodejs)

app.js

   const express = require('express');
    const upload = require('multer')({ dest: 'uploads/' });
    const path = require('path');
    const fs = require('fs');
    const port = 8088;
    
    let  app = express();
    
    app.set('port', port);
    // index.html, index.js放在static檔案夾中
    app.use(express.static(path.join(__dirname, 'static')));
    
    // app.get('/', (req, res) => {
    //   res.redirect('upload2.html');
    // });
    
    // 路由/ajax-upload 就回渲染 upload.html 頁面
    app.get('/ajax-upload', function(req, res){  
      res.sendFile('upload.html', { root: __dirname });  
    });  
    app.post('/upload', upload.single('test-upload'), (req, res) => {
      // 沒有附帶檔案
      if (!req.file) {
        res.json({ ok: false });
        return;
      }
    
      // 重命名檔案
      let oldPath = path.join(__dirname, req.file.path);
      let newPath = path.join(__dirname, 'uploads/' + req.file.originalname);
      fs.rename(oldPath, newPath, (err) => {
        if (err) {
          res.json({ ok: false });
          console.log(err);
        } else {
          res.json({ ok: true });
        }
      });
    });
    // 這裡還沒有做上傳取消 删除檔案的操作
    app.get('/abort', upload.single('test-upload'), (req, res)=>{
      console.log(req, 'abort') // 删除剛才上傳的檔案
    })
    
    app.listen(port, () => {
      console.log("[Server] localhost:" + port);
    });      

注:接口使用

  • 環境:nodejs
  • 項目結構 
    • static(圖檔如下) 
      • uploads
  • 架構包express: npm install express –save
  • 上傳包multer :npm install multer –save
  • 啟動: node app.js

以上代碼可以直接複制運作:

3、解釋

3.1.前端

上傳簡述: 前端的選擇檔案,掉後端上傳接口,利用ajax技術将檔案上傳的伺服器。
前端要知道後端是否上傳成功或者狀态,就得需要後端傳回給我們的狀态,最簡單的就是上傳成功或者失敗,再者需要知道進度的就要利用xhr 的程序方法了。
  • ajax 的原理就是利用浏覽器的XMLHttRequest 這個對象,因為IE浏覽器不是這對象,如果要相容的話 ,可以封裝一個XHR 對象,代碼可以看後面的附屬部分:
  • XMLHttRequest 對象有多個方法,監控ajax 上傳的進度主要利用這些屬性,來達到我們的目标
  • 下面是部分屬性(方法)介紹,這裡是MDN的介紹  xhr
    • onloadstart 擷取開始
    • onprogress 資料傳輸進行中
    • onabort 擷取操作終止
    • onerror 擷取失敗
    • onload 擷取成功
    • ontimeout 擷取操作在使用者規定的時間内未完成
    • onloadend 擷取完成(不論成功與否)

* 對xhr.upload.onprogress的解釋

這裡監控進度主要看這裡的屬性值的變化,如下圖 

具體的代碼片段解釋可以檢視我上面的代碼注釋

3.2.後端

這裡使用的nodejs的一個接口,之前我的用的是将前端預覽圖檔的base64的字元串傳到後端,然後後端解析這個base64的字元串,生成圖檔,儲存在磁盤中,但是會出現問題,進度不太好顯示,

是以查了相關資料,借用了某位大神的寫法,直接上傳文 件;還有一個就是multer包,利用這個進行上傳。

這裡項目也是一個後端渲染的方式,将頁面渲染好,然後發送給前端。

4、附屬代碼:

4.1.相容的XMLHttpRequest代碼段

function XMLHttpRequest(){
    if (typeof XMLHttpRequest != "undefined"){
    return new XMLHttpRequest();
    } else if (typeof ActiveXObject != "undefined"){
        if (typeof arguments.callee.activeXString != "string"){
        var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
        "MSXML2.XMLHttp"],
        i, len;
        for (i=0,len=versions.length; i < len; i++){
            try {
                new ActiveXObject(versions[i]);
                arguments.callee.activeXString = versions[i];
            break;
            } catch (ex){
                //跳過
            }
        }
    }
         return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error("No XHR object available.");
    }
}      

4.2.nodejs 處理base64字元串

node包:formidable 

imgBase64Arr: 前端傳值過來的 base64字元串的多張圖檔的數組

for(var i = 0;i<imgBase64Arr.length;i++) {
    var imgname    = util.randomStr(); // 随機字元串的方法
    imgname    = 'assets/img/'+ imgname + '.png';
    var base64     = imgBase64Arr[i].replace(/^data:image\/\w+;base64,/, "");
    //去掉圖檔base64碼前面部分data:image/png;base64
    var dataBuffer = new Buffer(base64, 'base64'); //把base64碼轉成buffer對象,
    console.log('dataBuffer是否是Buffer對象:'+Buffer.isBuffer(dataBuffer));
    fs.writeFile(imgname,dataBuffer,function(err){//用fs寫入檔案
      if(err){
          console.log(err);
      }else{
          console.log('圖檔上傳成功!');
      }
    })
    newimgarr.push(imgname.replace("assets",""));
}      

github位址:

https://github.com/adouwt/ajax-upload

前端上傳這塊已經封裝了一個基于vue的插件,

GitHub位址:

https://github.com/adouwt/vue-upload 

npm 官網上的資料顯示居然已經有一定的下載下傳量,實在很驚訝,不過也很歡迎大家學習使用,有bug,及時告知于我

https://www.npmjs.com/package/vue-ajax-upload

如有錯誤,敬請指出!

這裡提供了一個demo示範如下, ps:個人伺服器存儲較小,隻是用來展示,後端是個小白,沒有做一些檔案過濾和後端檔案風險校驗,大神請繞行哈,還有請手下留情哈!

戳我
上一篇: SparkR
下一篇: Spark Streaming

繼續閱讀