Showing posts with label JQuery. Show all posts
Showing posts with label JQuery. Show all posts

Practice: Using JSZip to Download Current Week's Images in Blogger


The Task
There is a blogger: its main content is pictures and photos, client wants to allow users to download today, this week and this month's pictures.

The Solution
The basic idea is to send a request to Blogger feed url, parse its json response to get all image links, then use JSZip and JQuery Deferred pattern to download all remote images.
Using GAE to Serve JavaScript Files
After download JSZip and JSZip-Utils, we need put them somewhere, so the blogger can load them. So we create one GAE application, and put them in the webapp/js/jszip folders.

We only want to load these scripts if the pageType is item: mean current page is a post page. So we add the following code under <b:includable id='post' var='post'>:

<b:includable id='post' var='post'>
<b:if cond='data:blog.pageType == "item"'>
<script src='//youapp.appspot.com/js/jszip/jszip.min.js'/>
<script src='//youapp.appspot.com/js/jszip/jszip-utils.min.js'/>
<script src='//youapp.appspot.com/js/jszip/FileSaver.js'/>
</b:if>

Add Download Links to Item Page
<div class='blog-pager' id='blog-pager'>
 <b:if cond='data:blog.pageType == "item"'>
  <div class='blog-pager' id='blog-pager'>
     <div id='blog-pager-newer-link'>
    <a class='blog-pager-newer-link' href='' id='downloadToday'>Download Today's Images</a>
   </div>

   <div id='blog-pager-older-link'>
    <a class='blog-pager-newer-link' href='' id='downloadMonth'>Download This Month's Images</a>
   </div>

  <div id='blog-pager-home-link'>
    <a class='blog-pager-newer-link' href='' id='downloadWeek'>Download This Week's Images</a>
  </div>

  </div>
 </b:if>
...
</div>
The Complete JavaScript Code
Using Blogger Feeds URL to Get Post in Range
Using JSZip and JQuery Deferred Pattern to Download Multiple Remote Images
<b:includable id='post' var='post'>
<b:if cond='data:blog.pageType == "item"'>
<script src='//lifelongprogrammertools.appspot.com/js/jszip/jszip.min.js'/>
<script src='//lifelongprogrammertools.appspot.com/js/jszip/jszip-utils.min.js'/>
<script src='//lifelongprogrammertools.appspot.com/js/jszip/FileSaver.js'/>
<script>
  var dateHeader= '<data:post.dateHeader/>';
  var parts = dateHeader.split('-');
  var postDate = new Date(parts[0], parts[1]-1, parts[2]);
  console.log("postDate:" + postDate);  
//<![CDATA[

$(function($) {
  if(JSZip.support.blob) {
   $("#downloadToday").click(function (e) {
   getTodayAllImages(dateHeader);
         e.preventDefault();
         return false;
   });
   $("#downloadWeek").click(function (e) {
   getWeekAllImages(postDate);
         e.preventDefault();
         return false;
   });
   $("#downloadMonth").click(function (e) {
   getMonthAllImages(postDate);
         e.preventDefault();
         return false;
   });

  }
});

function addToZip(zip, link, postUrl, i) {
var deferred = $.Deferred();

 JSZipUtils.getBinaryContent(link, function (err, data) {
    if(err) {
      alert("Problem happened when download image from " + postUrl + ", img link: " + link);
      console.error("Problem happened when download image from " + postUrl + ", img link: " + link);
      deferred.resolve(zip); // ignore this error: just logging
      // deferred.reject(zip);
    } else {
      zip.file("picture"+i+".jpg", data, {binary:true});
      deferred.resolve(zip);
    }
 });
return deferred;
}

function parseImagesRsp(root){
    var feed = root.feed;
  var parser = new DOMParser();  
  var zip = new JSZip();
  var deferreds = [];
  for(var i=0; i<feed.entry.length; i++)
  {
   var e=feed.entry[i];
   var doc = parser.parseFromString(e.content.$t, "text/html");
   var img = doc.querySelector("img");
   var pageUrl = e.link[2].href;
   if(!img)
   {       
     console.error("No img tag in " + pageUrl);  
   }
   else{
     var imgLink = img.src;
     console.log("img link: " + imgLink);
     deferreds.push( addToZip(zip, imgLink, pageUrl, i) );
   }
  }

$.when.apply(window, deferreds).done(generateZip);
}
function generateZip(zip)
{
 var content = zip.generate({type:"blob"});
 // see FileSaver.js
 saveAs(content, "downloadImages.zip");
}
function doDownload(startDate, endDate){
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = '/feeds/posts/default?published-min=' + startDate + '&published-max='+endDate+'&alt=json-in-script&max-results=1000&callback=parseImagesRsp';
    document.getElementsByTagName('head')[0].appendChild(script);
}
function getTodayAllImages(dateStr){
 var startDate = dateStr + "T00:00:01", endDate = dateStr + "T23:59:59";
    console.log("Download images from " + startDate + " to " + endDate);
    doDownload(startDate, endDate);
    return false;
}
function getMonthAllImages(dateObj){
 var startDate = yymmdd(getFirstDayOfCurrentMonth(dateObj)) + "T00:00:01", endDate = yymmdd(getLastDayOfCurrentMonth(dateObj))  + "T23:59:59";
    doDownload(startDate, endDate);
    return false;
}
function getWeekAllImages(dateObj){
 var startDate = yymmdd(getMondayOfCurrentWeek(dateObj)) + "T00:00:01", endDate = yymmdd(getSundayOfCurrentWeek(dateObj))  + "T23:59:59";
    doDownload(startDate, endDate);
    return false;
}
function yymmdd(dataObj){
  var month = (dataObj.getMonth()+1);
  var monthStr = month<10? ('0' + month): month;
  var dateStr = dataObj.getDate()< 10? ('0' + dataObj.getDate()): dataObj.getDate();
  return dataObj.getFullYear() + "-" + monthStr + "-" + dateStr;
}
function getMondayOfCurrentWeek(d)
{
    var day = d.getDay();
    return new Date(d.getFullYear(), d.getMonth(), d.getDate()-day + (day == 0 ? -6:1));
}
function getSundayOfCurrentWeek(d)
{
    var day = d.getDay();
    return new Date(d.getFullYear(), d.getMonth(), d.getDate()-day + (day == 0 ? 0:7));
}
function getFirstDayOfCurrentMonth(d){
  return new Date(d.getFullYear(), d.getMonth(), 1);
}
function getLastDayOfCurrentMonth(d){
  return new Date(d.getFullYear(), d.getMonth()+1, 0);
}
//]]>
</script>
</b:if>

Practice: Using JSZip to Download Multiple Remote Images


Using JSZipUtils.getBinaryContent
JSZipUtils provides getBinaryContent to fetch a file from remote server like below:
// loading a file and add it in a zip file
JSZipUtils.getBinaryContent("path/to/picture.png", function (err, data) {
   if(err) {
      throw err; // or handle the error
   }
   var zip = new JSZip();
   zip.file("picture.png", data, {binary:true});
});
Using JQuery Deferred Pattern to Wait all Ajax Request Finished
We will loop all image urls, and call getBinaryContent to add the remote image into the dest zip file. But getBinaryContent is an async call, it will return immediately. We have to wait until al ajax request fished: either succeeded or failed.

We can use JQuery Deferred pattern: 

When loop al image urls, we will call addToZip: it calls JSZipUtils.getBinaryContent and return a Deferred object, which will be set as resolved when the ajax request finished. 
We put all Deferred objects into an array, then use jQuery.when().done(generateZip) to execute the callback generateZip when all ajax requests finished.
The generateZip will generate the zip and call saveAs in FileSaver.js.
The Complete Code
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'/>
<script src='/js/jszip/jszip.min.js'/>
<script src='/js/jszip/jszip-utils.min.js'/>
<script src='/js/jszip/FileSaver.js'/>
<script>
function downloadAllImages(imgLinks){
 var zip = new JSZip();
 var deferreds = [];
 for(var i=0; i<imgLinks.length; i++)
 {
  deferreds.push( addToZip(zip, imgLinks[i], i) );
 }
 $.when.apply(window, deferreds).done(generateZip);
}
function generateZip(zip)
{
 var content = zip.generate({type:"blob"});
 // see FileSaver.js
 saveAs(content, "downloadImages.zip");
}
function addToZip(zip, imgLink, i) {
  var deferred = $.Deferred();
  JSZipUtils.getBinaryContent(imgLink, function (err, data) {
    if(err) {
      alert("Problem happened when download img: " + imgLink);
      console.erro("Problem happened when download img: " + imgLink);
      deferred.resolve(zip); // ignore this error: just logging
      // deferred.reject(zip); // or we may fail the download
    } else {
      zip.file("picture"+i+".jpg", data, {binary:true});
      deferred.resolve(zip);
    }
 });
return deferred;
}
</script>
Resources
jQuery.Deferred is the most important client-side tool you have
Understanding JQuery.Deferred and Promise
How to use JSZip
JSZipUtils.getBinaryContent

Labels

ANT (6) Algorithm (69) Algorithm Series (35) Android (7) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) JSON (7) Java (186) JavaScript (27) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) adsense (5) bat (8) regex (5) xml (5)