Export resized image in canvas to new JSZip package
Asked Answered
D

1

5

I can load an image into a canvas element and resize it, but I'm having trouble grabbing the resized image:

var logo = $(".logo"),
    loader = $(".load"),
    canvas = $(".holder"),
    ctx = canvas[0].getContext("2d");

function displayPreview(file) {
  var reader = new FileReader();

  reader.onload = function(e) {
    var img = new Image();
    img.src = e.target.result;
    img.onload = function() {
      // x, y, width, height
      ctx.drawImage(img, 0, 0, 128, 128);

      var dataURL = canvas[0].toDataURL("image/png");

      var logo = $(".logo");
      var imgUrl = dataURL;
      var imgz = $("<img>");
      imgz.attr("src", imgUrl);
      logo.html("");
      logo.append(imgz);
    };
  };
  reader.readAsDataURL(file);
}

into the download package function for jszip.

// Download Zip
$(".download").on("click", function(imgUrl) {
  var zip = new JSZip();
  zip.file("logo.png", imgUrl);
  var content = zip.generate({type:"blob"});
  // see FileSaver.js
  saveAs(content, "test.zip");
});

Snippet:

var logo = $(".logo"),
    loader = $(".load"),
    canvas = $(".holder"),
    ctx = canvas[0].getContext("2d");

function displayPreview(file) {
  var reader = new FileReader();

  reader.onload = function(e) {
    var img = new Image();
    img.src = e.target.result;
    img.onload = function() {
      // x, y, width, height
      ctx.drawImage(img, 0, 0, 128, 128);

      var dataURL = canvas[0].toDataURL("image/png");

      var logo = $(".logo");
      var imgUrl = dataURL;
      var imgz = $("<img>");
      imgz.attr("src", imgUrl);
      logo.html("");
      logo.append(imgz);
    };
  };
  reader.readAsDataURL(file);
}


$(document).ready(function() {
  loader.on("change", function(evt) {
    var file = evt.target.files[0];
    displayPreview(file);

    var reader = new FileReader();

    reader.onload = function(e) {
      // Download Zip
      $(".download").on("click", function(imgUrl) {
        var zip = new JSZip();
        zip.file("logo.png", imgUrl);
        var content = zip.generate({type:"blob"});
        // see FileSaver.js
        saveAs(content, "test.zip");
      });
      return false;
    };
    reader.readAsArrayBuffer(file);
  });

  // Trigger Load Image
  $(".trigload").click(function() {
    $("input").trigger("click");
  });
});
@import url("http://necolas.github.io/normalize.css/3.0.1/normalize.css");

.hide {
  display: none;
}

.logo {
  text-align: center;
}

.fill {
  width: 100%;
}

.fr {
  float: right;
}
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip/dist/jszip.min.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip-utils/dist/jszip-utils.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip/vendor/FileSaver.js"></script>

<input type="file" class="hide load">
<a class="trigload" href="javascript:void(0)">Load Image</a>
<a class="download fr" href="javascript:void(0)">Download</a>
<div class="logo"></div>
<div class="fill" align="center">
  <canvas class="holder" width="128" height="128"></canvas>
</div>
Dib answered 8/7, 2015 at 23:18 Comment(2)
Maybe you want to call the toDataURL() and all the following, only when the image has been drawn ? (in the img.onload handler)Pilchard
Thanks! :) That fixed the preview problem, but I'm still having trouble getting the resized image to download.Dib
P
12

In order to get JSZip to correctly save your dataURL to valid png file, it seems you need to add an object containing {base64: true}as third argument of the zip.file() method, and to remove the data:image/png;base64, from the dataURL.

Also, you were assigning the click event to the imgUrl variable. You may want to store it in a global variable, or check the $('.logo>img')[0].src or call once again canvas[0].toDataURL().

var logo = $(".logo"),
    loader = $(".load"),
    canvas = $(".holder"),
    ctx = canvas[0].getContext("2d");

function displayPreview(file) {
  var reader = new FileReader();

  reader.onload = function(e) {
    var img = new Image();
    img.src = e.target.result;
    img.onload = function() {
      // x, y, width, height
      ctx.drawImage(img, 0, 0, 128, 128);

      var dataURL = canvas[0].toDataURL("image/png");

      var logo = $(".logo");
      var imgUrl = dataURL;
      var imgz = $("<img>");
      imgz.attr("src", imgUrl);
      logo.html("");
      logo.append(imgz);
    };
  };
  reader.readAsDataURL(file);
}


$(document).ready(function() {
  loader.on("change", function(evt) {
    var file = evt.target.files[0];
    displayPreview(file);

    var reader = new FileReader();

    reader.onload = function(e) {
      // Download Zip
      $(".download").on("click", function() {
        var imgUrl = canvas[0].toDataURL();
        var zip = new JSZip();
        zip.file("logo.png", imgUrl.split('base64,')[1],{base64: true});
        var content = zip.generate({type:"blob"});
        // see FileSaver.js
        saveAs(content, "test.zip");   
      });
      return false;
    };
    reader.readAsArrayBuffer(file);
  });

  // Trigger Load Image
  $(".trigload").click(function() {
    $("input").trigger("click");
  });
});
@import url("http://necolas.github.io/normalize.css/3.0.1/normalize.css");

.hide {
  display: none;
}

.logo {
  text-align: center;
}

.fill {
  width: 100%;
}

.fr {
  float: right;
}
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip/dist/jszip.min.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip-utils/dist/jszip-utils.js"></script>
<script type="text/javascript" src="http://stuk.github.io/jszip/vendor/FileSaver.js"></script>

<input type="file" class="hide load">
<a class="trigload" href="javascript:void(0)">Load Image</a>
<a class="download fr" href="javascript:void(0)">Download</a>
<div class="logo"></div>
<div class="fill" align="center">
  <canvas class="holder" width="128" height="128"></canvas>
</div>

2022 Edit

JSZip now accepts Blobs as input directly, so it's better to convert your canvas to a Blob and pass this directly to JSZip. (As a fiddle because StackSnippets don't allow-downloads).

Pilchard answered 10/7, 2015 at 3:22 Comment(5)
since jszip now supports blob, shouldn't the answer now directly use blobs instead of base64 strings?Kutuzov
@Kutuzov yes definitely using Blobs is way better (and createImageBitmap instead of FileReader + <img>)Pilchard
thanks for the fast update! as a note to others, we had issues converting 10K canvas elements into blobs for jszip, so we stuck with the base64 option. this strangely was more memory efficient because of the blob promises. does this seem wrong to you @Kaiido?Kutuzov
Yes, it's at least surprising. I can't tell what JSZip does with the Blob under the hood, maybe this path is less efficient than their base64 one, but that sounds weird given that Blobs can be streamed. And for outside of JSZip Blobs should be more memory friendly than dataURLs...Pilchard
perhaps it's due to waiting on promises? here's the code: jsfiddle.net/8zbxcnvrKutuzov

© 2022 - 2024 — McMap. All rights reserved.