HTML5 Drag&Drop 同瀏覽器跨文件拖拉

 Sat, 09 Oct 2010 19:42:41 +0800

除了跨應用軟體間的drag&drop,另外一個可以測試的是同一瀏覽器但是不同視窗不同文件之間的drag&drop。其實使用的方式跟前面兩篇文章:HTML5 Drag&Drop + 純AJAX檔案上傳以及一個簡單的HTML5 Drag & Drop試作差不多。目前測試到比較可用的方式,是過濾Event.dataTransfer.types,看看裡面是否有一筆資料為'text/uri-list',然後用Event.dataTransfer.getData('text/uri-list')就可以取得拖拉檔案的網址,接下來就可以使用這個資料來做一些處理。

如果要讓網頁可以同時處理其他應用軟體與跨文件drag&drop,我想到比較好的方式是先透過Event.dataTransfer.types裡面是否有'Files'字串來判斷(DataTransfer.types)。但是這時發現Firefox4 Beta6有一個問題...Orz。在從另一個Firefox視窗拖曳圖檔進來時,並沒有在Event.dataTransfer.files設定檔案資料,但是Event.dataTransfer.types卻有一筆資料是'Files',這樣會造成原本的程式邏輯有問題。所以只好額外用Event.dataTransfer.files.length來判斷是否有檔案資料。

利用這樣的判斷方式,就可以在有圖檔的File物件收到時,上傳檔案並且顯示上傳後的檔案。如果收到的資料是圖檔的url,就利用這個url來顯示圖檔。以下是從前兩篇文章再做修改的測試:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
  span#panel {
    display: inline-block;
    background: #336699;
    width: 140px;
    height: 180px;
    border: solid black 2px;
    border-radius: 10px;
    padding: 5px;
    text-align: center;
    color: white;
    vertical-align: middle;
  }
  span {
    display: inline-block;
    vertical-align: middle;
  }
  div.msg {
    display: block;
    background: #6699CC;
    width: 480px;
    height: 50px;
    border: solid black 2px;
    border-radius: 10px;
    padding: 5px;
    text-align: center;
    color: white;
    margin: 1px;
    vertical-align: middle;
  }
  div#container {
    vertical-align: baseline;
    border: solid 1px white;
    padding: 5px;
    text-align: center;
  }
</style>
<script src="fwajax3.js"></script>
</head>
<body>
<div style="text-align:center"><h4>HTML5 Drag&Drop with pure AJAX file upload test.</h4></div>
<div id="container"><span id="panel">Drop image here.<br></span> <span><div class="msg" id="msg1"></div><div class="msg" id="msg2"></div><div class="msg" id="msg3"></div></span></div>
</body>
</html>
<script>
document.ondragenter = function(e){e.preventDefault();}
document.ondragover = function(e){e.preventDefault();}
document.getElementById('panel').addEventListener('dragenter', function(e){
  e.preventDefault();
  if(window.console) console.log('dragenter');
  e.effectAllowed = ['move'];
},false);
document.getElementById('panel').addEventListener('dragover', function(e){
  e.preventDefault();
  if(window.console) console.log('dragover');
  e.dataTransfer.dropEffect = 'move';
},false);
document.getElementById('panel').addEventListener('drop', function(e){
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move';
  if(window.console) console.log('drop');
  if(window.console) console.log(e.dataTransfer);
  if(e.dataTransfer.types && e.dataTransfer.types.length>0) {
    var isFile = false;
    for(var i=0; i<e.dataTransfer.types.length; i++) {
      if(e.dataTransfer.types[i] === 'Files' && e.dataTransfer.files.length>0) isFile = true;
    }
    for(var i=0; i<e.dataTransfer.types.length; i++) {
      if(window.console) console.log(e.dataTransfer.types[i]);
      if(e.dataTransfer.types[i]==='Files') {
        if(e.dataTransfer.files.length>0) {
          for(var j=0; j<e.dataTransfer.files.length; j++) {
            switch(e.dataTransfer.files[j].type) {
              case 'image/jpeg':
              case 'image/gif':
              case 'image/png':
              case 'image/bmp':
                document.getElementById('msg1').innerHTML = 'File name: <br>'+e.dataTransfer.files[j].name;
                document.getElementById('msg2').innerHTML = '';
                document.getElementById('msg3').innerHTML = '';
                setTimeout(function(blob){
                  return function() {
                    var fileReader = new FileReader();
                    fileReader.onload = function() {
                      if(window.console) console.log('got an image file.');
                      //if(window.console) console.log(this.result);
                      var uploader = new fwH5AjaxUploader(
                        'save_ajax.php',
                        function(_txt,_xml) {
                          if(window.console) console.log(_txt);
                          var msg = JSON.parse(_txt);
                          if(msg.state == 'success') {
                            document.getElementById('msg3').innerHTML = 'File uploaded: <br>'+blob.name;
                            var img = document.createElement('img');
                            img.src = msg.path;
                            img.width = '110';
                            img.style = "text-align:center";
                            document.getElementById('panel').innerHTML = 'Drop Here.<p>';
                            document.getElementById('panel').appendChild(img);
                            img = null;
                          }
                        }
                      );
                      uploader.addFile(
                        'fileupload',
                        {'name':blob.name,'type':blob.type,'data':this.result.slice(this.result.indexOf(",")+1)},
                        'base64'
                      );
                      document.getElementById('msg2').innerHTML = 'Start uploading: <br>'+blob.name;
                      uploader.send();
                    };
                    try {
                      fileReader.readAsDataURL(blob);
                    } catch(e) {
                      alert(e);
                    }
                  };
                }(e.dataTransfer.files[j]),100);
                break;
                default:
                alert('Only image file allowed.\nIncluding: jpeg, png, gif and bmp.');
                break;
            }
          }
        }
      }
      if(!isFile && e.dataTransfer.types[i]!=='Files') {
        var type = e.dataTransfer.types[i];
        if(window.console) console.log(type);
        if('text/uri-list' === type || 'text/x-moz-url' === type) {
          var msg = e.dataTransfer.getData(type);
          if(window.console) console.log(msg);
          var filename = msg.split('/')[msg.split('/').length-1].split('?')[0];
          var allowed = ['jpg','gif','png','bmp'];
          var isAllowed = false;
          for(var m=0; m<allowed.length; m++) {
            if(filename.toLowerCase().indexOf(allowed[m])>-1) {
              isAllowed = true;
              break;
            }
          }
          if(isAllowed) {
            var img = document.createElement('img');
            img.src = msg;
            img.width = '110';
            var panel = document.getElementById('panel');
            panel.innerHTML = 'Drop Here.<p>';
            panel.appendChild(img);
            document.getElementById('msg1').innerHTML = 'File name: <br>' + msg.split('/')[msg.split('/').length-1];
            document.getElementById('msg2').innerHTML = 'Linked from: <br>' + msg;
            document.getElementById('msg3').innerHTML = 'Linked.';
          } else {
            alert('Only image file allowed.\nIncluding: jpeg, png, gif and bmp.');
          }
        }
      }
    }
  }
},false);
</script>
save_ajax.php以及fwajax3.js請參考前篇,程式是一樣的。

接下來看一下操作的截圖:

這是跨文件拖拉圖檔,會直接使用url

這是從系統拖拉圖檔上傳的結果


2010-10-9 19:56 補充:

仔細觀察了一下DataTransfer物件,發現從Chrome7拖拉網頁中的圖檔到Firefox4時,會收到的資料主要是'text/uri-list',但是從Firefox4拖拉網頁中的圖檔到Chrome7時,會收到File物件及'text/uri-list'。我測試程式的邏輯有一點混亂,不過我不想再調整了...不過同時收到File物件及'text/uri-list'時,還是做一下判斷比較好,在不需要上傳檔案的情況下,也許直接使用url就可以了。