Jszip: read image files out of remote .zip

Created on 21 Feb 2016  Â·  5Comments  Â·  Source: Stuk/jszip

how do i read an image
(or image data, a la base64)
out of a remote .zip file?

and then use it as the src=
for an .html image tag?

here's an example of what i want to do:

http://zenmagiclove.com/simple/show5images.zip

that .zip has an .html file in it,
which loads in 5 image files,
which are also in that .zip file.

i can read out the .html file just fine:

var thefile=zip.file("show5images.html").asText()

and then i use jquery to stick that in a div like so:

$("#thehtmlfile").html(thefile)

but how do i extract each of the 5 .png files?

var pictfile=zip.file("1.png").as???()


and then, second related question, how do i
plug them in as the src for the 5 image tags?
equivalent to this jquery, to use a web image:

$("#img1").attr("src","http://zenmagiclove.com/simple/jaguar.jpg")

thank you so much for a fast zip library!

-bowerbird

questiosupport

Most helpful comment

About the image src/base64

A blob url will be way faster than using base64. I used the following setup to test your use-case:

a zip file show5images.zip containing 1.png to 5.png and show5images.html: an small html file only displaying these images.

Content of show5images.html inside the zip file:

<html>
<body>
  <img src="1.png">
  <img src="2.png">
  <img src="3.png">
  <img src="4.png">
  <img src="5.png">
</body>
</html>

Then, the main html page. I tried to describe everything in the comments, be sure to read them.

Content of index.html:

<!doctype html>
<html lang="en-us">
  <head><meta http-equiv=content-type content="text/html;charset=utf-8">
    <script src=http://code.jquery.com/jquery-1.11.2.js></script>
    <script type=text/javascript src=http://stuk.github.io/jszip/dist/jszip.js></script>
    <script type=text/javascript src=http://stuk.github.io/jszip-utils/dist/jszip-utils.js></script>
    <!--[if IE]>
    <script type=text/javascript src=http://stuk.github.io/jszip-utils/dist/jszip-utils-ie.js></script>
    <![endif]-->

    <script src="index.js"></script>
  </head>
  <body>
    <h1>loaded html:</h1>
    <div id="load_html"></div>
  </body>
</html>

Content of index.js:

jQuery(function($) {

  /**
   * Create an url for the current image.
   * @param {ZipObject} zipObject the object from JSZip containing the image.
   * @return {String} the url of the image.
   */
  function createImgUrl(zipObject) {
    // On recent browsers, using a blob will be way faster than a base64 url.
    // The support of blob urls (http://caniuse.com/#feat=bloburls) is more or
    // less the same as blob constructors (http://caniuse.com/#feat=blobbuilder)
    // so if we can use blobs, I expect we can use blob urls.
    if(JSZip.support.blob) {
      var imgBlob = new Blob([zipObject.asArrayBuffer()]);
      return URL.createObjectURL(imgBlob);
    } else {
      // if you don't need old browser support, please remove this branch !
      // /!\ warning, zipObject.asBase64() doesn't exist so I use here the
      // DEPRECATED JSZip.base64 object.
      // It will be removed in JSZip v3: you should then use zipObject.async("base64").
      var base64 = JSZip.base64.encode(zipObject.asBinary());
      // TODO: handle other mime types
      return "data:image/png;base64," + base64;
    }
  }

  /**
   * Read an html file inside a zip object and resolve relative images urls
   * from the zip file.
   * @param {String} htmlFileName the path to the html inside the zip file.
   * @param {JSZip} zip the JSZip object representing the zip file to use.
   * @return {jQuery} the jQuery object containing the parsed content.
   */
  function prepareContent(htmlFileName, zip) {
    var htmlContent = zip.file(htmlFileName).asText();

    // we wrap everything inside a div to avoid issues
    var $content = $("<div/>").append($.parseHTML(htmlContent));

    // for each image, update its url
    $content.find("img").each(function () {
      var $this = $(this);
      var imagePath = $this.attr("src");
      // find the image content inside the zip file
      if (zip.file(imagePath)) {
        var url = createImgUrl(zip.file(imagePath));
        $this.attr("src", url);
      } else {
        console.warn("the zip file doesn't contain", imagePath);
      }
    });
    return $content;
  }

  JSZipUtils.getBinaryContent("show5images.zip", function (err, data) {
    if(err) {
      throw err; // or handle the error
    }
    var zip = new JSZip(data);

    var $content = prepareContent("show5images.html", zip);
    $("#load_html").append($content);
  });
});

This code can of course be improved (only tested in Firefox/Chrome, it doesn't handle errors or nested html files for example) but should give you ideas.

About the documentation

Your use-case is not really typical but can be broken down in two steps:

  • manipulate the DOM, find and replace images (my method prepareContent, using mainly jQuery)
  • display the content of an image inside a zip file (my method createImgUrl, using JSZip)

I've seen enough people working on this second step to think it could be a useful example in our documentation.
It should be accessible to people that don't know everything. The goal is not to expect users to know all js APIs and just describe JSZip methods, but to help developers who wish to use JSZip.
It seems we need to work on our examples !

About your beer money offer

(this part reflects only my personal views) It's very kind of you but I see JSZip as a collective effort and I don't want money to interfere with a hobby :)

All 5 comments

as usual...

search for days and days, trying a hundred things,
and no luck at all...

then, break down in misery, and ask for help,
and you find the answer immediately after that.

david gave the necessary information here:

http://stackoverflow.com/questions/24957032/simple-jszip-create-zip-file-from-existing-images?rq=1

if anyone needs a full working demo, e-mail me:

[email protected]

again, thanks for jszip.js. it's smooth.
well, the documentation is too hard for
a stupid person like me to understand.
but the library itself is pure electricity...

-bowerbird

I'm glad my stackoverflow post helped !
Writing documentation is hard and a lot of "obvious" things for contributors are not obvious at all for other people. Here, your feedback is important: it's hard to look at your own code with pristine eyes.
What part of the documentation is hard to understand ? Is it the API itself ? Its use in real world situations (add remote content, extract image content, …) ? The first steps with it ? The structure ? Other ?
Don't omit details or remarks because you feels like stupid: the goal of the documentation is to explain to you how JSZip works. If you couldn't understand the documentation, then you're likely not the first one and we should improve it.

david-

i'm so glad you e-mailed me.

because even though i _thought_
i had solved my problem, i hadn't.

so i still need some help.... please.

but i believe i'm close to a solution, so
i will try that out, and then e-mail you
if i don't actually discover the answer.

i think it involves converting the image
data to base64 to stick into the "src=".


in the meantime, i will also ponder
the questions you asked me about
improving your documentation and
see if i can generate some advice...

off the top of my head, i would say
you could have more solid examples
for some "typical use-cases", but...
i suspect my own use-case is _not_
typical, so that doesn't help you out.

indeed, the problem is probably that,
as a long-time amateur coder-hacker
who can afford to ignore difficult stuff,
there are big holes in my knowledge...

so even if your docs can be improved,
i'm not a person who can tell you how.

-bowerbird

p.s. pleased and honored to hear from
someone who's helped so many people,
_including_ myself, on stack-overflow...
do please let me know how to send you
a bit of beer money for your kindness.

About the image src/base64

A blob url will be way faster than using base64. I used the following setup to test your use-case:

a zip file show5images.zip containing 1.png to 5.png and show5images.html: an small html file only displaying these images.

Content of show5images.html inside the zip file:

<html>
<body>
  <img src="1.png">
  <img src="2.png">
  <img src="3.png">
  <img src="4.png">
  <img src="5.png">
</body>
</html>

Then, the main html page. I tried to describe everything in the comments, be sure to read them.

Content of index.html:

<!doctype html>
<html lang="en-us">
  <head><meta http-equiv=content-type content="text/html;charset=utf-8">
    <script src=http://code.jquery.com/jquery-1.11.2.js></script>
    <script type=text/javascript src=http://stuk.github.io/jszip/dist/jszip.js></script>
    <script type=text/javascript src=http://stuk.github.io/jszip-utils/dist/jszip-utils.js></script>
    <!--[if IE]>
    <script type=text/javascript src=http://stuk.github.io/jszip-utils/dist/jszip-utils-ie.js></script>
    <![endif]-->

    <script src="index.js"></script>
  </head>
  <body>
    <h1>loaded html:</h1>
    <div id="load_html"></div>
  </body>
</html>

Content of index.js:

jQuery(function($) {

  /**
   * Create an url for the current image.
   * @param {ZipObject} zipObject the object from JSZip containing the image.
   * @return {String} the url of the image.
   */
  function createImgUrl(zipObject) {
    // On recent browsers, using a blob will be way faster than a base64 url.
    // The support of blob urls (http://caniuse.com/#feat=bloburls) is more or
    // less the same as blob constructors (http://caniuse.com/#feat=blobbuilder)
    // so if we can use blobs, I expect we can use blob urls.
    if(JSZip.support.blob) {
      var imgBlob = new Blob([zipObject.asArrayBuffer()]);
      return URL.createObjectURL(imgBlob);
    } else {
      // if you don't need old browser support, please remove this branch !
      // /!\ warning, zipObject.asBase64() doesn't exist so I use here the
      // DEPRECATED JSZip.base64 object.
      // It will be removed in JSZip v3: you should then use zipObject.async("base64").
      var base64 = JSZip.base64.encode(zipObject.asBinary());
      // TODO: handle other mime types
      return "data:image/png;base64," + base64;
    }
  }

  /**
   * Read an html file inside a zip object and resolve relative images urls
   * from the zip file.
   * @param {String} htmlFileName the path to the html inside the zip file.
   * @param {JSZip} zip the JSZip object representing the zip file to use.
   * @return {jQuery} the jQuery object containing the parsed content.
   */
  function prepareContent(htmlFileName, zip) {
    var htmlContent = zip.file(htmlFileName).asText();

    // we wrap everything inside a div to avoid issues
    var $content = $("<div/>").append($.parseHTML(htmlContent));

    // for each image, update its url
    $content.find("img").each(function () {
      var $this = $(this);
      var imagePath = $this.attr("src");
      // find the image content inside the zip file
      if (zip.file(imagePath)) {
        var url = createImgUrl(zip.file(imagePath));
        $this.attr("src", url);
      } else {
        console.warn("the zip file doesn't contain", imagePath);
      }
    });
    return $content;
  }

  JSZipUtils.getBinaryContent("show5images.zip", function (err, data) {
    if(err) {
      throw err; // or handle the error
    }
    var zip = new JSZip(data);

    var $content = prepareContent("show5images.html", zip);
    $("#load_html").append($content);
  });
});

This code can of course be improved (only tested in Firefox/Chrome, it doesn't handle errors or nested html files for example) but should give you ideas.

About the documentation

Your use-case is not really typical but can be broken down in two steps:

  • manipulate the DOM, find and replace images (my method prepareContent, using mainly jQuery)
  • display the content of an image inside a zip file (my method createImgUrl, using JSZip)

I've seen enough people working on this second step to think it could be a useful example in our documentation.
It should be accessible to people that don't know everything. The goal is not to expect users to know all js APIs and just describe JSZip methods, but to help developers who wish to use JSZip.
It seems we need to work on our examples !

About your beer money offer

(this part reflects only my personal views) It's very kind of you but I see JSZip as a collective effort and I don't want money to interfere with a hobby :)

i should have told you yesterday when i'd figured it out.

or maybe not, since your blob strategy is "way faster"...

i'll examine it, for later if i find out i need more speed,
but, so far, using base64 is fast enough. and simple.
(which is good for me, since i am a big fan of simple.)

anyway, i posted my base64 approach here:

http://zenmagiclove.com/simple/show5images.html

(those images are brought in via a timer, so don't think
that it's the base64 conversion that's taking that long.)

yes, my use-case primarily involves reading-and-display
of .zip files, posted online, which other people created...
(right now, it's .epub files, because i hack e-book apps.)

and yes, i guess this is atypical. most people seem to
want to create .zips by bundling up a bunch of files and
then downloading the resultant .zip to a users machine.
at least as far as i can discern.

anyway...

so my first stumbling block was just getting the contents
of a remote .epub. that's right, a simple list of files inside!

var daphyle=[]
$.each(zip.files,function(index,zipEntry)
{daphyle.push(zipEntry.name)})

i don't want to tell you how many hours i spent trying to
find that little loop. because it was lots and lots of 'em.
(i found it on some page demonstrating something else;
i'd tell you which, but i can never seem to get back to it.)

and then the current problem, which has a solution that
also boiled down to a few lines of code (in my approach):

var imagefile=zip.file(daphyle[xx]).asBinary()
b64="data:image/gif;base64,"+btoa(imagefile)
$("#img1").attr("src",b64)

anyway, there are two cases from the last two weeks
when i looked around for days to find a simple solution
for a simple problem that i think would be fairly common.

it could very well be the problem is me and my stupidity.

but if improving your documentation fixes human stupidity,
that would be a very good thing... :+)

-bowerbird

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ryanakl picture ryanakl  Â·  4Comments

Lsaes picture Lsaes  Â·  5Comments

jimmywarting picture jimmywarting  Â·  4Comments

xwLyc picture xwLyc  Â·  7Comments

sethdorris picture sethdorris  Â·  4Comments