Multiple Images to Zip File Download | JavaScript Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome to this tutorial so today what I'm going to be showing you is how you can download multiple files in one go by placing them in a zip file and then downloading that so in order to show you this I'm going to be downloading the images that appear on screen in both high and low resolution but I'm not going to be downloading all of the images every time I'm only going to be downloading those that user selects via the check boxes below the images so the underlying markup is three image elements with SRC values already set to low resolution versions of the images and directly below each image is an input element of type checkbox and below the main content images and check boxes there is a button that will Program start the download of the zip file when it is clicked so in JavaScript I've already selected the button and added an event listener to it listening out for a click event and when that occurs the a function that I've passed in in the second argument position it will run and I've already listed here the steps that I'm going to be following to download the images in zip format so the first of those is to store URLs to the images in an array so to do that I'm going to select all of the image elements in the Dom using query selector all so this is going to return to me all of the elements in an iterable list and what I want to do is to iterate through that list extracting the SRC value from each image so for that I can use for each so with for each you pass a function into it that will run as many times as there are items on the iterable and each time the function runs you have available to you the current item so to get the SRC value of each item in an array I'll create a new empty array before calling for each and then inside the 4-H function push the SRC value of the current item into the URLs array now at the moment all SRC values are being pushed into the array but I want the pushing of each value to be conditional upon whether a user has selected an image via its checkbox so to do that we're going to introduce an if statement here nesting the pushing inside of it and the condition here is going to be whether the check box next to an image is checked so we can Traverse the Dom to access the checkbox for an image using next element sibling and check if it's currently ticked you can query the check property which will return true or false so if it returns true then the SRC or an image will be pushed into the array so let's see whether this is working so we're getting the expected SRC values in the array however you might want these URLs point to high resolution versions of the images so instead of pushing SRC values into the array instead I'm going to be pushing asks to high resolution images so relative to index.html they are in the folder images forward slash high res and they have the same file names as the logos images that the SRC values are pointing to so what I'm going to do here is extract the file name from the end of the SRC and then append it to the path to the high res images so to get the file name on the SRC I can use the slice method passing in the last index position or a forward slash in the SRC so this will give me the file name including the forward slash So to avoid that I can increase the index value that I'm slicing at by one and then I can push that value seated by the path to the hi-res images into the URLs array so I'm using spring literal syntax to construct the string so let's test this so now relative paths to the high-res images are being pushed into the array and it's actually a good thing that their relative URLs because it means that your script is not anchored to only work on one particular domain name you may have noticed that the SRC values workable URLs but these were generated also on the basis of relative URLs which were the SRC values or the images in the HTML okay so now that we have the URLs in an array we're ready to move on to the next step which is getting the image data so to be doing this I'm going to be using the native fetch function and I'm going to be making multiple fetches for the image data to the URLs that are in the array so each fetch request is going to return a promise and I want to wait until each of those has resolved until I zip the image data so wait for multiple promises you can use the auto method on the native promise object so this accepts an array of promises which you're waiting for and it also returns a promise which results when all the promises might be passed into it resolved so delete the result or promise to All I'm going to use the await keyword and to enable that I need to use the async keyword before the function then which is nested so I'll store a reference to its result when it comes through and before that create the array of promises to pass in to promise.all so I want to patch the data from each of the URLs returning an array of promises that will eventually resolve that I can pass into promise.all so I'm going to call map on urls and the function that I passed in here will run as many times as there are URLs each time I have each item available to me and the return value of map is an array of each return value each time the function runs so what I'm going to do is do the fetch inside here and make the result the return value of the function so I'm going to use the async keyword before this function this means that the function immediately returns a promise that will immediately resolve when I provide a return value inside the function so first I want to fetch the data from each of the URLs so batch returns a promise and I can use a waiting to wait for the result because we are inside an asynchronous function so patch returns a response object with a readable stream on it that will contain the file data and what we want to do is to read that in into a file container so we can do that by calling blob on the response object so this is going to read the file data into a blob object which in JavaScript is a container of raw binary data and we can use the data in that format to create a zip file so I'm going to make the return value of the function a blob each time and this is going to be the value that each promise resolves to in the array that map returns so we can now pass that in to promise.all promise.all is going to wait for all of the promises to resolve and it's going to return the results into an array so the end result is going to be an array of file data in blog format so let's test if this is working foreign blocks logged to the console each one of Type image JPEG and we also get the size of each in bytes so we now have the image data in an array and we can move on to the next step which is creating a zip file of that image data so to create the zip file I'm going to rely upon a well-established library or doing that JS dip so you can install this via npm or you can download the jsip script and link to it locally in this tutorial though I'm going to be importing it into my project via a CDN link so I'll place the link in the head section of my HTML and then down in my script I will have available to me a new object and that object is JS zip so this is an object instructor so if I call it with the new keyword before it this is going to create a new object that'll save under the reference off zip and it's this object that I can use to create a new zip file so to add a file you call the file method passing in as a first argument a file name and secondly the file data now in this case we want to add all of the blogs from the array that was generated in the last step so I'm going to call for each on the blogs array and what I have available to me each time inside the orange function is each block and what I want to do each time the function runs is ADD current blog to zip so the second argument here inside this function should be blob or the file name I know it's going to be the jpeg every time but I want to create a unique file name here so what I'm going to do is make use of a second parameter that you have available to you in orreach.jpg now if you want to generate some folder structure what you can do is call the folder method entering the name of the folder that you want to create store a reference to the folder that you have created and then you can call the file method on that reference to add a file into that folder so you could place files in here but in this example I'm just going to create a simple text document now the zip object that we've been working with isn't actually a zip file yet so we still need to generate that and we can do that by calling the generate async method on the zip object you've created and you need to enter here the output data format so a Blog can be downloaded as a file on the front end and as the name suggests generate async is an asynchronous process it returns a promise which we can wait for the result of using the await keyword and the end result is going to be the zip file ready to download so let's take a look at it in a console so we've created a Blog file that can be downloaded of type application zip though the final step is to download this blob so I'm going to pass blob into a function that I would Define outside of this one so this keeps the previous function a bit less congested and it's reusable functionality so I could use or something else so to initiate a user download you can create a new anchor element setting the downloaded review to the file name that you want to save it under so in this case I'll save it as test dot zip a hrep attribute should point to the file so this should be a URL we don't have one for the file but you can create a temporary URL to a blob in browser memory using Create object URL passing the blog into it so I'll store reference to the URL it creates and set that as the hrep of the anchor element now what we're going to be doing is simulating a click on that element so to do that you can call the click method now in most browsers this will work but in some browsers you have to add the element to the Dom first so to be on the safe side I'll end the anchor element to the Dom before simulating the click and directly afterwards I can remove it from the Dom again and make sure that the element is not visible to the user you can set a display style of now and it would be good practice here to remove the temporary URL from memory once the download starts because while it exists the file that it's pointing to is also being held in memory so after the download starts or a particular zip file it's no longer necessary and you can remove to the URL and Associated oil from memory like this using revoke object URL so let's test this now when I click the download button that should start a download of the images I select in zip format so you can see down here that that is what is happening so if we inspect the zip file here we have two jpeg images correspond to the ones that we selected on the page and we also have this sub folder with the readme file inside it now if you're working with images like I am here and you want to download the images that appear on the page then I have a little trick view that will prevent you having to make fetch requests or the image data so to do that you can draw each of the images onto a HTML canvas and then extract each of them in blog format the downside is that it introduces an image processing step in the client so if it's very important to you to have the original image data downloaded in a zip then you probably want to make a fetch request for the original data but if you're okay with processed versions of the images and you want to avoid making a fetch request this is something that you might want to employ so instead of creating a URLs array here I'm going to create an array of plots of the image data so what I'm going to do inside this for each function is draw each image that user has selected onto a canvas so now that I've created a new canvas element here I need to get the context for the canvas because you don't draw directly on to the canvas you draw onto the context next you want to set the width and the height of the canvas to the Natural width and height of the image so to the dimensions of the original image not how it appears on the page so now we're ready to draw the image onto the context on the canvas so the first argument is the image element the second and third are where you want to start drawing on the X and Y axis specified in pixels so if you want to cover the whole canvas you want to start drawing at the beginning of both axes now to extract the data from the canvas as a blob equal to blob on the canvas and you enter a callback function here where the blob is available to you now this process of generating a blob it takes a little bit of time and is asynchronous so you want to wait for all of the Vlogs to be available before you place them in a zip file so instead of pushing bulbs directly into the blogs array what I'm going to push into there is promises that was result to block values so to do that I'm going to push each time into the blogs array a new promise so immediately what goes in the array is a promise that will eventually resolve and then inside the promise I create a blob and when I have the blob available I call res that ends the promise and the value that it should result to is the plot so the values in the blogs array will initially be promises so what we want to do here is to wait for them all to resolve so we can do that using promise.all like we did all the batches so promise.all returns a promise and I'll store a reference to that I no longer need any of this batch code so I'm going to comment that out also there is no URLs array now one change that I'm going to make to the code here before I test it is to change the file extension when I'm adding each blob to the zip file because when you extract a blob from the canvas by default it will be in PNG format you can try to generate a Blog in a different format by adding a second argument when calling the two block function where you specify a mind type or the image format that you'd like to produce but browser support is not guaranteed for any format other than PNG if you try to produce a format that the browser doesn't support then it will default back to PNG so the safest option is to stick with the PNG format and just before we test again I noticed that the image file names aren't starting at zero so I can add plus one here so they'll start from one so let's test this now and start the download so this triggers the download of a zip file this time the images are in PNG format so they're a little smaller than in the last example because these are low resolution versions of the images but as you can see they resemble quite well the images that appear on screen even though they've gone through the processing step of being drawn to the canvas and then extracted from it so if you're happy with this quality then you can use this trick to avoid a pitch request or images that have already loaded onto the page so that's it for this tutorial on starting user download or multiple files using the example of images I hope you found this useful if you did please consider hitting the like button down below this video it helps us with the algorithm and others to find this video and if you'd like to see more content like this from us in the future don't forget you can subscribe to the channel
Info
Channel: OpenJavaScript
Views: 5,391
Rating: undefined out of 5
Keywords:
Id: 83YNsdRzlqQ
Channel Id: undefined
Length: 19min 43sec (1183 seconds)
Published: Wed Mar 22 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.