1. 程式人生 > >HTML5 postMessage+iframe實現檔案跨域非同步上傳

HTML5 postMessage+iframe實現檔案跨域非同步上傳

前段時間因為專案需求,要實現圖片的跨域上傳,研究了一番後實現了這個功能,這裡做個記錄方便日後遇到同樣的問題,也給後來者做個參考。

目前為止我瞭解到的檔案非同步上傳的方式有以下幾種

  • flash控制元件 (IOS已經不支援flash了)
  • HTML5 FormData
  • iframe形式

當然我們還可以通過使用一些第三方的外掛來實現跨域上傳比如jQuery的ajaxFileUpload,不過好像也是通過構建iframe來實現非同步上傳的。通過form的target屬性將返回結果顯示在一個隱藏的iframe來實現類似ajax的效果,而且form表單的提交是可以跨域的。

以下是檔案上傳的html:

<!DOCTYPE html>
<html>
<head>
    <title>Html5 postMessage+iframe跨域上傳檔案</title>
    <meta charset='UTF-8'>
    <link rel="stylesheet" href="style.css"/>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.js"></script>
</head>
<body>

    <form class="form w_12" action="" method="post" enctype="multipart/form-data"  target="uploadimg_ifm">
        <div class="form-group w_12">
            <label class="label fl w_12 tl"> 圖片上傳:</label>
            <div class="fr w_12">
                <label class="btn-primary btn w_12" for="img">選擇圖片</label>
                <input type="file" id="img" accept="image/*" style="display:none" class="w_8" name="img">
                <input type="hidden" class="imgurl" value="">
                <div id="uploader-demo">
                    <!--用來存放item-->
                    <div id="fileList" class="uploader-list"></div>
                </div>
            </div>
        </div>
    </form>
    <iframe name="uploadimg_ifm" style="display:none;"></iframe>
    <script src="jqthumb.min.js"></script>
</body>
</html>

加入js程式碼實現圖片預覽

var origin = "http://localhost:8088";
var host = "http://localhost:8088/fileupload/";
$("#img").change(function(){
    var file = this.files[0]; //選擇上傳的檔案
    var r = new FileReader();
    r.readAsDataURL(file); //Base64
    $(r).load(function(){
         var $li = $(
            '<div class="file-item thumbnail">' +
                '<img src="'+ this.result +'" alt="" />' +
                '<div class="info">' + file.name + '</div>' +
            '</div>'
            );
        $('#fileList').html( $li );
        $('#fileList img').jqthumb();
        //立即上傳圖片
        $(".form").attr("action",host+"servlet/FileUploadServlet").submit();
    });
});

重點來了 加入postMessage訊息監聽 顯示上傳結果

window.addEventListener("message", function( event ) {
    if(event.origin != origin){
        return false;
    }
    var data = event.data;
    if (data.result == "success") {
        $(".imgurl").val(data.url);
        $('<div class="success">上傳成功</div>').appendTo( "#fileList .file-item" );
    }else if (data.result == "failed") {
        $('<div class="error">上傳失敗</div>').appendTo( "#fileList .file-item" );
    }
}, false);

後臺這裡採用cos上傳元件

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    PrintWriter out = resp.getWriter();
    String filePath = getServletContext().getRealPath("/") + "upload";  
    System.out.println(filePath); 
    File uploadPath = new File(filePath);  
    // 檢查資料夾是否存在 不存在 建立一個  
    if (!uploadPath.exists()) {  
        uploadPath.mkdir();  
    }
    // 檔名  
    String fileName = "";  
    // 上傳檔案數  
    int fileCount = 0;  
    // 上傳檔案  
    MultipartRequest multipartRequest = new MultipartRequest(req, filePath, maxPostSize, encoding, fileRenamePolicy);//取得上傳檔案  
    Enumeration files = multipartRequest.getFileNames();   
    while (files.hasMoreElements()) {  
        String name = (String) files.nextElement(); 
        fileName = multipartRequest.getFilesystemName(name);
        String contentType = multipartRequest.getContentType(name);
        fileCount = (fileName != null) ? ++fileCount : fileCount;
        System.out.println("檔名:" + fileName);  
        System.out.println("檔案型別: " + contentType);   
    }
    System.out.println("共上傳" + fileCount + "個檔案!");  
    String data = "";
    if (fileCount >= 1) {
        data = "{\"result\":\"success\",\"url\":\"upload/"+fileName+"\"}";
        //返回執行js,呼叫HTML5 postmessage通知父框架上傳結果 第二個引數為接收訊息的物件視窗的 URL 地址,可以在 URL地址字串中使用萬用字元'*'指定全部。
        out.print("<script>var iframeWin = parent.window;iframeWin.postMessage("+data+", \"*\");</script>");
    }else{
        data = "{\"result\":\"failed\",\"url\":\"\"}";
        out.print("<script>var iframeWin = parent.window;iframeWin.postMessage("+data+", \"*\");</script>");
    }
}

效果圖:

好了,以上就是主要實現的程式碼,並附上完整版下載連結fileupload.rar。如有不足之處還請留言指正…