HTML5,JavaScript:从外部窗口拖放文件(Windows资源管理器)

Bun*_*ori 32 javascript html5 drag-and-drop draggable

我可以请一个HTML5文件拖放实现的好例子吗?如果从外部应用程序(Windows资源管理器)到浏览器窗口执行拖放操作,源代码应该有效.它应该在尽可能多的浏览器上工作.

我想问一个有很好解释的示例代码.我不想使用第三方库,因为我需要根据我的需要修改代码.代码应基于HTML5和JavaScript.我不想使用JQuery.

我花了一整天寻找好的材料来源,但令人惊讶的是,我没有找到任何好的东西.我发现的示例适用于Mozilla,但不适用于Chrome.

Dwa*_*yne 80

这是一个简单的例子.它显示一个红色方块.如果将图像拖到红色方块上,则会将其附加到正文.我已经确认它适用于IE11,Chrome 38和Firefox 32.有关更详细的说明,请参阅Html5Rocks文章.

var dropZone = document.getElementById('dropZone');

// Optional.   Show the copy icon when dragging over.  Seems to only work for chrome.
dropZone.addEventListener('dragover', function(e) {
    e.stopPropagation();
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';
});

// Get file data on drop
dropZone.addEventListener('drop', function(e) {
    e.stopPropagation();
    e.preventDefault();
    var files = e.dataTransfer.files; // Array of all files

    for (var i=0, file; file=files[i]; i++) {
        if (file.type.match(/image.*/)) {
            var reader = new FileReader();

            reader.onload = function(e2) {
                // finished reading file data.
                var img = document.createElement('img');
                img.src= e2.target.result;
                document.body.appendChild(img);
            }

            reader.readAsDataURL(file); // start reading the file data.
        }
    }
});
Run Code Online (Sandbox Code Playgroud)
<div id="dropZone" style="width: 100px; height: 100px; background-color: red"></div>
Run Code Online (Sandbox Code Playgroud)

  • 如果你没有取消dragover事件,那么该元素将不是一个有效的放置目标 - 所以第一个监听器实际上不是可选的(如果没有它在Firefox中,这个片段对我来说不起作用).请参阅https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#drop (7认同)

Bun*_*ori 15

此链接非常详细地解释了我的问题:

http://www.html5rocks.com/en/tutorials/file/dndfiles/

  • 你应该避免纯粹的链接式答案.链接很容易破坏,使得答案对于未来的访问者来说都是无用的. (38认同)

Per*_*alJ 8

接受的答案为该主题提供了一个很好的链接。但是,根据SO规则,应避免使用纯链接答案,因为它们可能随时消失。因此,我花了一些时间来总结链接的内容,以供将来的读者阅读。


入门

在实施将文件上传到您的网站的方法之前,您应确保选择支持的浏览器能够完全支持File API。您可以使用下面的Javascript代码快速进行测试:

// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}
Run Code Online (Sandbox Code Playgroud)

您当然可以修改上面的代码片段,以满足您的需求。


表格输入

上传文件的最常见方法是使用标准<input type="file">元素。JavaScript将选定File对象的列表作为返回FileList

// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}
Run Code Online (Sandbox Code Playgroud)
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
Run Code Online (Sandbox Code Playgroud)


拖放

对上面的代码片段进行简单的修改,就可以提供拖放支持。

<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
Run Code Online (Sandbox Code Playgroud)
  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.dataTransfer.files; // FileList object.

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }

  // Setup the dnd listeners.
  var dropZone = document.getElementById('drop_zone');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
Run Code Online (Sandbox Code Playgroud)


读取文件

Now you've obtained a reference to the File, you can instantiate a FileReader to read its contents into memory. When the load completes the onload event is fired and its result attribute can be used to access the file data. Feel free to look at the references for FileReader to cover the four available options for reading a file.

The example below filters out images from the user's selection, calls reader.readAsDataURL() on the file, and renders a thumbnail by setting the src attribute to a data URL.

<div id="drop_zone">Drop files here</div>
<output id="list"></output>
Run Code Online (Sandbox Code Playgroud)
  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // Loop through the FileList and render image files as thumbnails.
    for (var i = 0, f; f = files[i]; i++) {

      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }

      var reader = new FileReader();

      // Closure to capture the file information.
      reader.onload = (function(theFile) {
        return function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = ['<img class="thumb" src="', e.target.result,
                            '" title="', escape(theFile.name), '"/>'].join('');
          document.getElementById('list').insertBefore(span, null);
        };
      })(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
Run Code Online (Sandbox Code Playgroud)
  .thumb {
    height: 75px;
    border: 1px solid #000;
    margin: 10px 5px 0 0;
  }
Run Code Online (Sandbox Code Playgroud)


Slicing

In some cases reading the entire file into memory isn't the best option. For example, say you wanted to write an async file uploader. One possible way to speed up the upload would be to read and send the file in separate byte range chunks. The server component would then be responsible for reconstructing the file content in the correct order.

The following example demonstrates reading chunks of a file. Something worth noting is that it uses the onloadend and checks the evt.target.readyState instead of using the onload event.

<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
Run Code Online (Sandbox Code Playgroud)
  function readBlob(opt_startByte, opt_stopByte) {

    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }

    var file = files[0];
    var start = parseInt(opt_startByte) || 0;
    var stop = parseInt(opt_stopByte) || file.size - 1;

    var reader = new FileReader();

    // If we use onloadend, we need to check the readyState.
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) { // DONE == 2
        document.getElementById('byte_content').textContent = evt.target.result;
        document.getElementById('byte_range').textContent = 
            ['Read bytes: ', start + 1, ' - ', stop + 1,
             ' of ', file.size, ' byte file'].join('');
      }
    };

    var blob = file.slice(start, stop + 1);
    reader.readAsBinaryString(blob);
  }
  
  document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
    if (evt.target.tagName.toLowerCase() == 'button') {
      var startByte = evt.target.getAttribute('data-startbyte');
      var endByte = evt.target.getAttribute('data-endbyte');
      readBlob(startByte, endByte);
    }
  }, false);
Run Code Online (Sandbox Code Playgroud)
  #byte_content {
    margin: 5px 0;
    max-height: 100px;
    overflow-y: auto;
    overflow-x: hidden;
  }
  #byte_range { margin-top: 5px; }
Run Code Online (Sandbox Code Playgroud)


Monitoring Progress

One of the nice things that we get for free when using async event handling is the ability to monitor the progress of the file read; useful for large files, catching errors, and figuring out when a read is complete.

The onloadstart and onprogress events can be used to monitor the progress of a read.

The example below demonstrates displaying a progress bar to monitor the status of a read. To see the progress indicator in action, try a large file or one from a remote drive.

<input type="file" id="files" name="file" /> Read bytes: 
<span class="readBytesButtons">
  <button data-startbyte="0" data-endbyte="4">1-5</button>
  <button data-startbyte="5" data-endbyte="14">6-15</button>
  <button data-startbyte="6" data-endbyte="7">7-8</button>
  <button>entire file</button>
</span>
<div id="byte_range"></div>
<div id="byte_content"></div>
Run Code Online (Sandbox Code Playgroud)
  var reader;
  var progress = document.querySelector('.percent');

  function abortRead() {
    reader.abort();
  }

  function errorHandler(evt) {
    switch(evt.target.error.code) {
      case evt.target.error.NOT_FOUND_ERR:
        alert('File Not Found!');
        break;
      case evt.target.error.NOT_READABLE_ERR:
        alert('File is not readable');
        break;
      case evt.target.error.ABORT_ERR:
        break; // noop
      default:
        alert('An error occurred reading this file.');
    };
  }

  function updateProgress(evt) {
    // evt is an ProgressEvent.
    if (evt.lengthComputable) {
      var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
      // Increase the progress bar length.
      if (percentLoaded < 100) {
        progress.style.width = percentLoaded + '%';
        progress.textContent = percentLoaded + '%';
      }
    }
  }

  function handleFileSelect(evt) {
    // Reset progress indicator on new file selection.
    progress.style.width = '0%';
    progress.textContent = '0%';

    reader = new FileReader();
    reader.onerror = errorHandler;
    reader.onprogress = updateProgress;
    reader.onabort = function(e) {
      alert('File read cancelled');
    };
    reader.onloadstart = function(e) {
      document.getElementById('progress_bar').className = 'loading';
    };
    reader.onload = function(e) {
      // Ensure that the progress bar displays 100% at the end.
      progress.style.width = '100%';
      progress.textContent = '100%';
      setTimeout("document.getElementById('progress_bar').className='';", 2000);
    }

    // Read in the image file as a binary string.
    reader.readAsBinaryString(evt.target.files[0]);
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
Run Code Online (Sandbox Code Playgroud)
  #progress_bar {
    margin: 10px 0;
    padding: 3px;
    border: 1px solid #000;
    font-size: 14px;
    clear: both;
    opacity: 0;
    -moz-transition: opacity 1s linear;
    -o-transition: opacity 1s linear;
    -webkit-transition: opacity 1s linear;
  }
  #progress_bar.loading {
    opacity: 1.0;
  }
  #progress_bar .percent {
    background-color: #99ccff;
    height: auto;
    width: 0;
  }
Run Code Online (Sandbox Code Playgroud)


Cha*_*III 4

查看 ondragover 事件。您可以简单地将 div 的内部隐藏起来,直到 ondragover 事件触发一个函数,该函数将显示带有 in 的 div,从而让用户拖放文件。在 上添加 onchange 声明可以让您在将文件添加到输入时自动调用函数(例如上传)。确保输入允许多个文件,因为您无法控制它们将尝试拖动到浏览器中的数量。