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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10261989/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:33:36  来源:igfitidea点击:

HTML5, JavaScript: Drag and Drop File from External Window (Windows Explorer)

javascripthtmldrag-and-dropdraggable

提问by Bunkai.Satori

Can I kindly ask for a good working example of HTML5 File Drag and Dropimplementation? The source code should work if drag and drop is performed from external application(Windows Explorer) to browser window. It should work on as many browsers as possible.

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

I would like to ask for a sample code with good explanation. I do not wish to use third party libraries, as I will need to modify the code according to my needs. The code should be based on HTML5 and JavaScript. I do not wish to use JQuery.

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

I spent the whole day searching for good source of material, but surprisingly, I did not find anything good. The examples I found worked for Mozilla but did not work for Chrome.

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

回答by EricP

Here is a dead-simple example. It shows a red square. If you drag an image over the red square it appends it to the body. I've confirmed it works in IE11, Chrome 38, and Firefox 32. See the Html5Rocksarticle for a more detailed explanation.

这是一个非常简单的例子。它显示一个红色方块。如果您将图像拖到红色方块上,它会将其附加到正文。我已经确认它适用于 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.
        }
    }
});
<div id="dropZone" style="width: 100px; height: 100px; background-color: red"></div>

回答by Emma

The accepted answer provides an excellent linkfor this topic; however, per SO rules, pure link answers should be avoided since they could die out at any time. For this reason, I have taken the time to summarize the content of the link for future readers.

接受的答案为该主题提供了极好的链接;但是,根据 SO 规则,应避免纯链接答案,因为它们随时可能消失。出于这个原因,我花时间为未来的读者总结了链接的内容。



Getting Started

入门

Prior to implementing a method to upload files to your website, you should ensure that the browsers you choose to support will be capable of fully supporting the File API. You can test this quickly with the snippet of Javascript below:

在实施将文件上传到您的网站的方法之前,您应该确保您选择支持的浏览器能够完全支持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.');
}

You can modify the snippet above to meet your needs of course.

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



Form Input

表单输入

The most common way to upload a file is to use the standard <input type="file">element. JavaScript returns the list of selected Fileobjects as a FileList.

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

  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);
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>



Drag and Drop

拖放

Making simple modifications to the snippet above allows us to provide drag and drop support.

对上面的代码片段进行简单的修改允许我们提供拖放支持。

  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);
<div id="drop_zone">Drop files here</div>
<output id="list"></output>



Reading Files

读取文件

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

现在您已经获得了对 的引用File,您可以实例化 以FileReader将其内容读入内存。当加载完成时,onload事件被触发,其result属性可用于访问文件数据。随意看看引用FileReader涵盖用于读取文件的四个选择。

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

下面的示例从用户的选择中过滤掉图像,调用reader.readAsDataURL()文件,并通过将src属性设置为数据 URL 来呈现缩略图。

  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);
  .thumb {
    height: 75px;
    border: 1px solid #000;
    margin: 10px 5px 0 0;
  }
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>



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 onloadendand checks the evt.target.readyStateinstead of using the onloadevent.

以下示例演示读取文件的块。值得注意的是,它使用onloadend和 检查evt.target.readyState而不是使用onload事件。

  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);
  #byte_content {
    margin: 5px 0;
    max-height: 100px;
    overflow-y: auto;
    overflow-x: hidden;
  }
  #byte_range { margin-top: 5px; }
<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>



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 onloadstartand onprogressevents can be used to monitor the progress of a read.

onloadstartonprogress事件可用于监控读取进度。

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.

下面的示例演示了显示进度条以监控读取状态。要查看正在运行的进度指示器,请尝试使用大文件或远程驱动器中的文件。

  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);
  #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;
  }
<input type="file" id="files" name="file" />
<button onclick="abortRead();">Cancel read</button>
<div id="progress_bar"><div class="percent">0%</div></div>

回答by Charles John Thompson III

Look into ondragover event. You could simply have a inside of a div that is hidden until the ondragover event fires a function that will show the div with the in it, thus letting the user drag and drop the file. Having an onchange declaration on the would let you automatically call a function (such as upload) when a file is added to the input. Make sure that the input allows for multiple files, as you have no control over how many they are going to try and drag into the browser.

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