無重新整理上傳檔案以及HTML5下的實現方式

技術小甜發表於2017-11-15

常規實現方法:

ajax無法實現上傳檔案,因此常規情況下,要實現無重新整理上傳檔案的做法,是在頁面隱藏一個iframe,然後將上傳form的target指向這個iframe,變相的實現。如下程式碼:

1
2
3
4
5
6
7
<p id="uploading" style="display:none;">Uploading...<img src="loading.gif"/>
<form action="upload.php" method="post" enctype="multipart/form-data" target="upload_target" onsubmit="startUpload();" >
    File: <input name="myfile" type="file" />
          <input type="submit" name="submitBtn" value="Upload" />
</form>
<iframe id="upload_target" name="upload_target" src="#" style=";height:0;border:0px solid #fff;">
</iframe>

upload_target是一個長寬都是0的iframe,所以頁面上看不見他。還需要配合js,使得效果更好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
function startUpload(){
   $(`#uploading`).show();
}
function finisheUpload(i){
   $(`#uploading`).hide();
   if(i==0)
   {
     alert("上傳成功");
    }
    else
   {
      alert("上傳失敗");
    }
}
</script>

其中startUpload方法是在提交表單的時候觸發,而由於沒有什麼回撥函式,因此finisheUpload只能由upload.php檔案的輸出控制。通常就是在輸出中輸出一段javascript程式碼來執行。

php程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
header("Content-Type:text/html;charset=utf-8");
$destination_path getcwd().DIRECTORY_SEPARATOR;
$filname $destination_path basename$_FILES[`myfile`][`name`]);
$filname=iconv("UTF-8","gb2312",$filname);
if(move_uploaded_file($_FILES[`myfile`][`tmp_name`], $filname)) {
   echo "<script language="javascript" type="text/javascript">window.parent.finisheUpload(0);</script> ";
}
else
{
   echo "<script language="javascript" type="text/javascript">window.parent.finisheUpload(1);</script> ";
}


注意,輸出javascript的時候,由於呼叫的js方法是在iframe外定義的,要在iframe內呼叫js方法,需要指定window.parent。

————————

HTML5下的實現方法:

先介紹一下FileReader物件:

FileReader物件的詳細說明可以在W3C官方文件中檢視。

該介面提供方法來讀取檔案物件或者Blob物件。它繼承了EventTarget,介面的描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[Constructor]
    interface FileReader: EventTarget {
      // async read methods
      void readAsArrayBuffer(Blob blob);
      void readAsText(Blob blob, optional DOMString label);
      void readAsDataURL(Blob blob);
      void abort();
      // states
      const unsigned short EMPTY = 0;
      const unsigned short LOADING = 1;
      const unsigned short DONE = 2;
      readonly attribute unsigned short readyState;
      // File or Blob data
      readonly attribute (DOMString or ArrayBuffer)? result;
      readonly attribute DOMError? error;
      // event handler attributes
      attribute EventHandler       attribute EventHandler onprogress;
      attribute EventHandler       attribute EventHandler onabort;
      attribute EventHandler onerror;
      attribute EventHandler     };

可以看到有4個非同步方法,其中3個是讀取,1個是放棄,4個狀態屬性,1個result,1個error和6個事件。(之前還有readAsBinaryString方法,不過已經被W3C去除了)這6個事件的觸發時機如下:

loadstart –When the read starts.

progress –While reading (and decoding) blob

abort –When the read has been aborted. For instance, by invoking the abort() method.

error –When the read has failed (see errors).

load –When the read has successfully completed.

loadend –When the request has completed (either in success or failure).

下面演示一個例子,讀取一個文字文件,並且alert出來內容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
function readfile(dom)
{
    var file = dom.files[0];
    var textType = /text.*/;//正規表示式,使之匹配text/html,text/plain
    if (file.type.match(textType)) {
    var reader = new FileReader();
    //註冊事件函式,即等讀完內容後,要做的事情
    reader.onload = function(e) {
        alert(reader.result);
    }
    //非同步讀取內容
    reader.readAsText(file,`gb2312`);
        $("#msg").html("正在執行非同步讀取");
    }
    else {
        alert("檔案不支援");
    }
}
</script>
<input type="file" id="testfiles" name="files[]" onchange="readfile(this);" />
<div id="msg"></div>

——————————————————————————————

下面演示一個例子,讀取一個DataURL的,DataURL其實是一種DataURI(要知道URI的更多細節,可以去http://css-tricks.com/data-uris/,或者維基百科瞭解更多)。它提供了一種在瀏覽器中顯示資料的途徑。比如你要顯示一個圖片(百度的logo)的話,你可以如下寫:

1
<img src="http://www.baidu.com/img/bdlogo.gif" />

你也可以用它的URI去寫,如下:

<img src=”data:image/gif;base64,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″ />

src中填寫的字串就是DataURI,格式如下:

data:[<mime type>][;charset=<charset>][;base64],<encoded data>

這種URI其實很有用的,可以減少HTTP請求,使得網站提速。因此獲取了URI的話就可以載入本地的影像了。示例程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
function loadimg(dom)
{
    var file = dom.files[0];
    //正規表示式,使之匹配image/jpeg等
    var imageType = /image.*/;
    if (file.type.match(imageType)) {
        var reader = new FileReader();
        reader.onload = function(e) {
        var img = new Image();
        img.src = reader.result;
        $("#divimg").append(img);
    }
    reader.readAsDataURL(file);
    }
    else {
        alert("檔案不支援");
    }
}
</script>
<input type="file" id="testfiles" name="files[]" onchange="loadimg(this);" />
<div id="divimg"></div>


————————————————————————————————————

還有一個方法是readAsArrayBuffer,從字面就可以看出,是把檔案讀取到一個陣列緩衝區。

使用readAsArrayBuffer這種方法實現上傳檔案

下面演示一個例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
function upload()
{
    var file = $(`#testfiles`)[0].files[0];
    var reader = new FileReader();
    reader.onload = function (rResult) {
        var filename = file.name;
        var options = {
            type: `POST`,
            url: `upload.php?filename=`+filename,
            data: reader.result,
            success:function(result){
                alert(result.msg);
            },
            processData: false,  // 告訴jQuery不要去處理髮送的資料
            contentType: false,   // 告訴jQuery不要去設定Content-Type請求頭
            dataType:"json"
        };
        $.ajax(options);
    };
    reader.readAsArrayBuffer(file);
}
</script>
<input type="button" value="upload" onclick="javascript:upload();"/>

後端PHP程式碼:

1
2
3
4
5
6
7
8
9
10
try{
    $filename=$_GET[`filename`];
    $input file_get_contents("php://input");  //這個是獲取請求的InputStream,PHP下的寫法
    file_put_contents($filename$input);//儲存成檔案。
    echo json_encode(array("msg"=>"上傳成功"));
}
catch(Exception $e)
{
    echo json_encode(array("msg"=>"上傳失敗"));
}


FormData方法

FromData的官方說明在這裡。利用FormData物件,你可以使用一系列的鍵值對來模擬一個完整的表單。

以下給出一個例子,允許上傳多個檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
function upload()
{
    var formdata = new FormData();
    $.each($(`#testfiles`)[0].files, function(i, file) {
    formdata.append(`file-`+i, file);
    });
    var options = {
        type: `POST`,
        url: `upload.php`,
        data: formdata,
        success:function(result){
            alert(result.msg);
        },
        processData: false,  // 告訴jQuery不要去處理髮送的資料
        contentType: false,   // 告訴jQuery不要去設定Content-Type請求頭
        dataType:"json"
    };
    $.ajax(options);
}
</script>
<input type="button" value="upload" onclick="javascript:upload();"/>


後臺PHP程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
try
{
    foreach($_FILES as $key => $value)
    {
        //print_r ($_FILES[$key]); echo "<br>";
        move_uploaded_file( $value["tmp_name"], $value[`name`]);
    }
        echo json_encode(array("msg"=>"上傳成功"));
}
catch(Exception $e)
{
    echo json_encode(array("msg"=>"上傳失敗"));
}



參考文件:

https://developer.mozilla.org/zh-CN/docs/DOM/XMLHttpRequest/FormData/Using_FormData_Objects

http://dev.w3.org/2006/webapi/FileAPI/#FileReader-interface

http://www.w3.org/TR/XMLHttpRequest2/#interface-formdata

http://blog.teamtreehouse.com/reading-files-using-the-html5-filereader-api

http://www.dotblogs.com.tw/junegoat/archive/2013/05/27/test-fileapi-multiupload-readasarraybuffer.aspx


本文轉自cnn23711151CTO部落格,原文連結:http://blog.51cto.com/cnn237111/1330089,如需轉載請自行聯絡原作者


相關文章