使用 Blob 和 msSaveBlob 以本地方式保存文件

使用 Blob 和 msSaveBlob 以本地方式保存文件

本主题从使用 Web 存储以本地方式保存文件停止的位置开始,演示 如何使用 Blob 构造函数以及 window.navigator.msSaveBlob 和window.navigator.msSaveOrOpenBlob 方法以本地方式保存文件(任意大小)。

本主题包含下列部分:

注意  以下示例要求使用 Internet Explorer 10 或更高版本。
 

使用 BlobBuilder API 创建 Blob

使用 Blob 构造函数可以直接在客户端上创建和操作 Blob(通常等效于一个文件)。Internet Explorer 10 的 msSaveBlob 和 msSaveOrOpenBlob 方法允许用户在客户端上保存文件,方法如同从 Internet 下载文件,这是此类文件保存到“下载”文件夹的原因。

请注意,msSaveBlob 和 msSaveOrOpenBlob 之间的区别就在于前者仅为用户提供“保存”按钮,而后者不但提供“保存”按钮,还提供“打开”按钮。

为了帮助了解 Blob 构造函数和 msSaveBlob/msSaveOrOpenBlob 如何用于在客户端上保存更改的文件,请考虑以下示例:

示例 1

<!DOCTYPE html>
<html>

<head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Example 1</title>
</head>

<body>
  <h1>Example 1</h1>
  <script>
    var blobObject = new Blob(["I scream. You scream. We all scream for ice cream."]); 
    
    window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt'); // The user only has the option of clicking the Save button.
    alert('File save request made using msSaveBlob() - note the single "Save" button below.');
    
    var fileData = ["Before you insult a person, walk a mile in their shoes. That way, when you insult them, you'll be a mile away - and have their shoes."];
    blobObject = new Blob(fileData);
    window.navigator.msSaveOrOpenBlob(blobObject, 'msSaveBlobOrOpenBlob_testFile.txt'); // Now the user will have the option of clicking the Save button and the Open button.
    alert('File save request made using msSaveOrOpenBlob() - note the two "Open" and "Save" buttons below.');
  </script>
</body>

</html>


使用 Blob()构造函数,我们首先创建一个 Blob 对象,其参数是包含所需文件内容的数组:

var blobObject = new Blob(["I scream. You scream. We all scream for ice cream."]);

我们接下来从 blobObject 复制该内容,并将其保存到一个文本文件(该文件保存在下载文件夹中):

window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt');

注意   如果用户提供可以如此操作的权限(通过单击相应信息栏中关联的提示),则只能将  msSaveBlob_testFile.txt 保存到 Downloads 文件夹。
 

然后,使用 msSaveOrOpenBlob 方法重复该过程,该方法为用户提供了“保存”和“打开”选项。

下例添加 Blob 功能检测:

示例 2

<!DOCTYPE html>
<html>

<head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Example 2</title>
</head>

<body>
  <h1>Example 2</h1>
  <script>
    function requiredFeaturesSupported() {
      return ( BlobConstructor() && msSaveOrOpenBlobSupported() );
    }
    
    function BlobConstructor() {
      if (!window.Blob) {
        document.getElementsByTagName('body')[0].innerHTML = "<h1>The Blob constructor is not supported - upgrade your browser and try again.</h1>";
        return false;
      } // if
      
      return true;
    } // BlobConstructor
    
    function msSaveOrOpenBlobSupported() {
      if (!window.navigator.msSaveOrOpenBlob) { // If msSaveOrOpenBlob() is supported, then so is msSaveBlob().
        document.getElementsByTagName('body')[0].innerHTML = "<h1>The msSaveOrOpenBlob API is not supported - try upgrading your version of IE to the latest version.</h1>";            
        return false;
      } // if
      
      return true;
    } // msSaveOrOpenBlobSupported
        
    if (requiredFeaturesSupported()) {
      var blobObject = new Blob(["I scream. You scream. We all scream for ice cream."]);
      
      window.navigator.msSaveBlob(blobObject, 'msSaveBlob_testFile.txt');
      alert('File save request made using msSaveBlob() - note the single "Save" button below.');
      
      var fileData = ["Before you insult a person, walk a mile in their shoes. That way, when you insult them, you'll be a mile away - and have their shoes."];
      blobObject = new Blob(fileData);
      window.navigator.msSaveOrOpenBlob(blobObject, 'msSaveBlobOrOpenBlob_testFile.txt');
      alert('File save request made using msSaveOrOpenBlob() - note the two "Open" and "Save" buttons below.');
    }
  </script>
</body>

</html>

将文件保存到客户端后,下一步是从保存的文件中检索数据。以下示例基于读取本地文件使用 Web 存储以本地方式保存文件,允许创建一个基于 canvas 的绘图,将其保存到本地文件并显示此类保存的绘图。此示例通常按照如下方式使用:

  1. 使用鼠标或手指(仅触摸设备)在框内创建一个绘图。
  2. 单击“保存”按钮,然后在“信息栏”中单击得到的“保存”按钮。
  3. 通过单击 x 关闭第二个“信息栏”
  4. 单击“擦除”按钮。
  5. 单击“加载”按钮,然后单击得到的“浏览”按钮并选择以前保存的绘图文件。随即将显示保存的绘图。

请注意,绘图保存后,跳过第 4 步可允许用户合成多个绘图。

示例 3

<!DOCTYPE html>
<html>

<head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Example 3</title>
  <style>
    html { 
     -ms-touch-action: none; /* Capture all touch events for our own purposes. */
     text-align: center;
    }
  
    #hideWrapper { 
      display: none; /* Do not show the file picker dialog until we're ready to do so. */
    }    
  </style>
</head>

<body>
  <h1>Example 3</h1>
  <canvas id="drawingSurface" width="500" height="500" style="border:1px solid black;">
  </canvas> <!-- The canvas element can only be manipulated via JavaScript -->
  <div>
    <button id="erase">Erase</button>
    <button id="save">Save</button>
    <button id="load">Load</button>
  </div>  
  <div id="hideWrapper">
    <p>Select one of your saved canvas drawings to display:</p>
    <input type="file" id="fileSelector" /> <!-- By design, if you select the exact same files two or more times, the 'change' event will not fire. -->
  </div>    
  <script>
    function requiredFeaturesSupported() {
      return ( 
               BlobConstructorSupported() && 
               msSaveOrOpenBlobSupported() && 
               canvasSupported() && 
               fileApiSupported()
             );
    } // requiredFeaturesSupported
    
    function BlobConstructorSupported() {
      if (!window.Blob) {
        document.getElementsByTagName('body')[0].innerHTML = "<h1>The Blob constructor is not supported - upgrade your browser and try again.</h1>";
        return false;
      } // if
      
      return true;
    } // BlobConstructorSupported
    
    function msSaveOrOpenBlobSupported() {
      if (!window.navigator.msSaveOrOpenBlob) { // If msSaveOrOpenBlob() is supported, then so is msSaveBlob().
        document.getElementsByTagName('body')[0].innerHTML = "<h1>The msSaveOrOpenBlob API is not supported - upgrade Internet Explorer and try again.</h1>";            
        return false;
      }
      
      return true;
    } // msSaveOrOpenBlobSupported
        
    function canvasSupported() {
      if (!document.createElement('canvas').getContext) {
        document.getElementsByTagName('body')[0].innerHTML = "<h1>Canvas is not supported - upgrade your browser and try again.</h1>";                  
        return false;
      }
      
      return true;
    } // canvasSupported  
    
    function fileApiSupported() {
      if (document.getElementById('fileSelector').files && window.FileReader) {
        return true;        
      }
      else {
        document.getElementsByTagName('body')[0].innerHTML = "<h1>The File API is not sufficiently supported - upgrade your browser and try again.</h1>";            
        return false;      
      }
    } // fileSelectorSupported      
        
    if ( requiredFeaturesSupported() ) {    
      var canvas = document.getElementById('drawingSurface'); // A static variable, due to the fact that one or more local functions access it.
      var context = canvas.getContext('2d'); // A static variable, due to the fact that one or more local functions access it.

      context.fillStyle = "purple"; // Because purple is cool.

      if (window.navigator.msPointerEnabled) {
        canvas.addEventListener('MSPointerMove', paintCanvas, false);
      }
      else {
        canvas.addEventListener('mousemove', paintCanvas, false);
      }

      document.getElementById('erase').addEventListener('click', eraseCanvas, false);
      document.getElementById('save').addEventListener('click', saveCanvas, false);
      document.getElementById('load').addEventListener('click', loadCanvas, false);
      
      document.getElementById('fileSelector').addEventListener('change', handleFileSelection, false); // Add an onchange event listener for the <input id="fileSelector"> element.      
    } // if ( requiredFeaturesSupported() )
    
    function paintCanvas(event) { // The "event" object contains the position of the pointer/mouse.
      context.fillRect(event.offsetX, event.offsetY, 4, 4); // Draw a 4x4 rectangle at the given coordinates (relative to the canvas box). As of this writing, not all browsers support offsetX and offsetY.
    }

    function saveCanvas() {
      var drawingFileName = "canvas" + Math.round( (new Date()).getTime() / 1000 ) + ".txt"; // Produces a unique file name every second.
      var blobObject = new Blob( [canvas.toDataURL()] ); // Create a blob object containing the user's drawing.
      
      window.navigator.msSaveBlob(blobObject, drawingFileName); // Copy the blob object content and save it to a file.
      document.getElementById('hideWrapper').style.display = 'none'; // Remove the file picker dialog from the screen if the Save button gets clicked.      
    } // saveCanvas

    function eraseCanvas() {
      context.clearRect(0, 0, context.canvas.width, context.canvas.height);
      document.getElementById('hideWrapper').style.display = 'none'; // Remove the file picker dialog from the screen if the Erase button gets clicked.
    } // eraseCanvas

    function loadCanvas() {
      document.getElementById('hideWrapper').style.display = 'block'; // Unhide the file picker dialog so the user can select a saved canvas drawing to load into the canvas element.   
    } // loadCanvas      
    
    function handleFileSelection(evt) {    
      var files = evt.target.files; // The file selected by the user (as a FileList object).

      if (!files) {
        alert("The selected file is invalid - do not select a folder. Please reselect and try again.");
        return;
      }

      // "files" is a FileList of file objects. Try to display the contents of the selected file:
      var file = files[0]; // The way the <input> element is set up, the user cannot select multiple files.
      
      if (!file) {
        alert("Unable to access " + file.name.toUpperCase() + "Please reselect and try again."); 
        return;
      }
      if (file.size == 0) {
        alert("Unable to access " + file.name.toUpperCase() + " because it is empty. Please reselect and try again.");
        return;
      }
      if (!file.type.match('text/.*')) {
        alert("Unable to access " + file.name.toUpperCase() + " because it is not a known text file type. Please reselect and try again.");
        return;
      }
      
      // Assert: we have a valid file.
      
      startFileRead(file); // Asychronously fire off a file read request.      
      document.getElementById('hideWrapper').style.display = 'none'; // Remove the file picker dialog from the screen since we have a valid file.      
    } // handleFileSelection
    
    function startFileRead(fileObject) {
      var reader = new FileReader(); //

      // Set up asynchronous handlers for file-read-success, file-read-abort, and file-read-errors:
      reader.onloadend = displayDrawing; // "onloadend" fires when the file contents have been successfully loaded into memory.
      reader.abort = handleFileReadAbort; // "abort" files on abort.
      reader.onerror = handleFileReadError; // "onerror" fires if something goes awry.

      if (fileObject) { // Safety first.
        reader.readAsText(fileObject); // Asynchronously start a file read thread. Other supported read methods include readAsArrayBuffer() and readAsDataURL().
      }
      else {
        alert("fileObject is null in startFileRead().");
      }
    } // startFileRead
    
    function displayDrawing(evt) {
      var img = new Image(); // The canvas drawImage() method expects an image object.
  
      img.src = evt.target.result; // Obtain the file contents, which was read into memory (whose format is a text data URL string).
      // eraseCanvas(); To allow composite drawings, remove this comment.
      img.onload = function() { // Only render the saved drawing when the image object has fully loaded the drawing into memory.
        context.drawImage(img, 0, 0); // Draw the image starting at canvas coordinate (0, 0) - the upper left-hand corner of the canvas.
      } // img.onload */
    } // displayFileText
      
    function handleFileReadAbort(evt) {
      alert("File read aborted.");
    } // handleFileReadAbort

    function handleFileReadError(evt) {
      switch (evt.target.error.name) {
        case "NotFoundError":
          alert("The file could not be found at the time the read was processed.");
          break;
        case "SecurityError":
          alert("A file security error occured.");
          break;
        case "NotReadableError":
          alert("The file cannot be read. This can occur if the file is open in another application.");
          break;
        case "EncodingError":
          alert("The length of the data URL for the file is too long.");
          break;
        default:
          alert("File error code " + evt.target.error.name);
      } // switch
    } // handleFileReadError
  </script>
</body>

</html>

你可能尚未注意到,示例 3 中的一个特征是无法连续两次重新加载同一绘图文件。即,无法执行以下过程:

  1. 绘图显示时,单击“擦除”
  2. 使用鼠标或手指(仅触摸设备)在框内创建一个绘图。
  3. 单击“保存”按钮,然后在“信息栏”中单击得到的“保存”按钮。
  4. 通过单击 x 关闭第二个“信息栏”
  5. 单击“擦除”按钮。
  6. 单击“加载”按钮,然后单击出现的“浏览”按钮并选择在第 3 步中创建的绘图文件。随即将显示保存的绘图。
  7. 单击“擦除”按钮。
  8. 单击“加载”按钮,然后单击得到的“浏览”按钮并选择在第 6 步中选择的同一绘图文件。此时不会显示该绘图。

此行为是故意如此设计的,你可以轻松解决此问题。第一步是将 <input type="file" id="fileSelector" /> 元素放到 <form> 元素中,如下所示:

<div id="hideWrapper">
  <p>Select one of your saved canvas drawings to display:</p>
  <form>
    <input type="file" id="fileSelector" />
  </form>
</div>    
<script>

下一步是通过单击“加载”按钮时调用表单的 reset() 方法来清除表单子元素中之前的任何用户输入。

function loadCanvas() {
  document.querySelector('#hideWrapper > form').reset(); // Allow the input element to pick the same file consecutively more than once.    
  document.getElementById('hideWrapper').style.display = 'block'; // Unhide the file picker dialog so the user can select a saved canvas drawing to load into the canvas element.   
} // loadCanvas

此处提供此更新的示例:示例 4 (右键单击网页并选择“查看源”以查看它的源代码)。

同样,示例 5 通过改进简单的绘图“应用”以及将 canvas.toDataURL() 替换为 canvas.msToBlob() 来扩展示例 4。使用 canvas.toDataURL(),可以预防使用其他 应用程序轻松查看已保存绘图的可能性。将绘图保存为 PNG 文件的好处是允许很多标准应用程序(包括浏览器)显示绘图。通过切换到 canvas.msToBlob(),我们可以直接将文件 保存为 PNG 格式,如下所示:

function saveCanvas() {
  var drawingFileName = "canvas" + Math.round( (new Date()).getTime() / 1000 ) + ".png"; // Produces a unique file name every second.
  
  window.navigator.msSaveBlob(globals.canvas.msToBlob(), drawingFileName); // Save the user's drawing to a file.
  document.getElementById('filePickerWrapper').style.display = 'none'; // Remove the file picker dialog from the screen since we just saved the user's file.
} // saveCanvas

此外,将文件保存为 PNG 格式可允许我们将 startFileRead(file)(及其三个关联的 回调函数)替换为以下四行代码:

img.src = window.URL.createObjectURL(file);
img.onload = function() { 
  globals.context.drawImage(img, 0, 0); 
  window.URL.revokeObjectURL(this.src); 
}

此处提供完整的示例:示例 5(右键单击网页并 选择“查看源”以查看它的源代码)。

我们得出结论,使用 Windows Internet Explorer 的 msSaveBlob 和 msSaveOrOpenBlob 方法以及 Blob 构造函数,可以在客户端上保存修改的文件 - 这在 Internet Explorer 10 之前相对较难。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值