Toms Online Notebook Sharing my stuff.

Zip Download Servlet

by tgutwin


Posted on Sunday Oct 16, 2016 at 12:33PM in Programming


Java LogoI needed a simple Java Servlet to zip a directory of files (or a single file) with parameters to dynamically specify what to zip up.

It does NOT create a temp file; it zips and streams the output directly to the client.

its called ZipFiles.

Here it is...

/*
 *  $HeadURL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/servlet/ZipFiles.java $
 *  $Revision: 1105 $
 *  $LastChangedDate: 2016-05-16 19:19:44 -0700 (Mon, 16 May 2016) $
 *  $LastChangedBy: tgutwin $
 *  Copyright (c) 2016 Tom B. Gutwin P.Eng. North Vancouver BC Canada
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 3
 *  of the License, or any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; If not, see <http://www.gnu.org/licenses/>.
 */
package ca.bc.webarts.servlet;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.ParameterParser;
import com.oreilly.servlet.ParameterNotFoundException;
import com.oreilly.servlet.ServletUtils;


/**
 * <h1>Downloading single or Multiple Files As Zip file.</h1>
 *
 * <h2>3 steps to implement</h2><ol><li>add this class to WEB-INF/classes</li>
 * <li>add the following to your web.xml file<pre>
  &lt;servlet&gt;
    &lt;servlet-name&gt;ZipFiles&lt;/servlet-name&gt;
       &lt;servlet-class&gt;ca.bc.webarts.servlet.ZipFiles&lt;/servlet-class&gt;
    &lt;/servlet&gt;
    &lt;servlet-mapping&gt;
      &lt;servlet-name&gt;ZipFiles&lt;/servlet-name&gt;
      &lt;url-pattern&gt;/zipfiles/*&lt;/url-pattern&gt;
    &lt;/servlet-mapping&gt;
  &lt;/servlet&gt;
 </pre></li><li>Add the URL link to your webpage (html or jsp). The syntax (based on the mapping you setup in the web.xml)
 * and parameters are as follows:<ul><li>http://&lt;yourWebServerAddress>/&lt;yourWebAppContextName&gt;<b>/zipfiles/?filename=&lt;fileToZip&gt;&dirname=&lt;suDirWhereFileLives&gt;</b></li>
 * <li> example: http://warp4.webarts.bc.ca/tunes/ZipFiles/?filename=AThousandSuns_03-Radiance.ogg&dirname=mythTunes/LinkinPark/AThousandSuns</li>
 * <li> NOTE: filename param is OPTIONAL. if NOT provided, this servlet will zip all files in the dirname.</li>
 * </ul>
 * </li>
 * </ol>
 * <h3> This code is Copyright &copy; 2016 Tom B. Gutwin P.Eng. North Vancouver BC Canada,</h3><h4>and relesed under the <a href="http://www.gnu.org">GNU</a>
 * <a href="http://www.gnu.org/licenses/licenses.html#GPL">General Public License (GNU GPL)</a>.</h4>
 */
public class ZipFiles extends HttpServlet
{

  private static final long serialVersionUID = -2767248383788052391L;
  /** Class constant. **/
  protected static final String className_ = "ZipFiles";
  private final static String SYSTEM_FILE_SEPERATOR = File.separator;
  /**  Version String.  */
  private final static String SERVLET_VERSION = "0.01_[$Rev: 1105 $]";

  /**  Build String. (yymmddhhss)  */
  private final static String BUILD_TAG = "161015143718";

  private static String webServerHostName_ = "warp4.webarts.bc.ca";

  private boolean debugOut_ = false;
  /** flag if the parms have specified a file (true) or a dir (false) to zip. **/
  private boolean fileZip_ = true;
  /** flag if the parms are valid. **/
  private boolean validParms_ = true;

  /**  controls if extra info is dumped to system.out. **/
  private String debugOut = "";
  /** not used. **/
  private String browserName = "";
  /** not used. **/
  private String browserVersion = "";

  /** Servlet parameter to specify the specific file(name) to zip up and return. **/
  private String filename = "";
  /** Servlet parameter to specify the specific directory to zip up and return; OR if a filename is ALSO specified, the dire where the file exists. **/
  private String dirname = "";
  private String someParamYouWannaSet_ = "";

  /**
   *  Gets the ServletInfo attribute of this servlet
   *
   * @return    The ServletInfo value
   */
  public String getServletInfo()
  {
    final String methodName = "getServletInfo";
    return "WebARTS Design ZipFiles servlet. Version:" + SERVLET_VERSION +
        "  Build:" + BUILD_TAG;
  }


  /**
   * The one time servlet init stuff goes here. It sets the derbyDBDir based on the following prioritized varables:
   * <ol><li>context init param: derbyDBDir</li><li>servlet init param (from web.xml): derbyDBDir</li>
   * <li>default hardcoded variable: derbyDBDir</li></ol>If defined in multiple places, the higher priority item will
   * be used.
   **/
  public void init()
  {
    System.out.println("\n~~~~~~~~\n~~~~~~~~\nInitializing ca.bc.webarts.servlet.ZipFiles\n~~~~~~~~\n~~~~~~~~");

    boolean notFoundInit = true;
    boolean notFoundContext = true;
    java.util.Enumeration <String> initEnum = getInitParameterNames();
    for (; notFoundInit && initEnum.hasMoreElements();)
    {
      if(initEnum.nextElement().equals("someParamYouWannaSet"))
      {
        notFoundInit = false;
        /* Do something with the init param */
        someParamYouWannaSet_ = getInitParameter("someParamYouWannaSet");
        System.out.println("\n~~~~~~~~\n     INIT ServletParam: someParamYouWannaSet="+someParamYouWannaSet_);
      }
    }

    //also check context
    initEnum = getServletConfig().getServletContext().getInitParameterNames();
    for (; notFoundContext && initEnum.hasMoreElements();)
    {
      if(initEnum.nextElement().equals("someParamYouWannaSet"))
      {
        notFoundContext = false;
        /* Do something with the init param */
        someParamYouWannaSet_ = getServletConfig().getServletContext().getInitParameter("baseDir");
        System.out.println("\n~~~~~~~~\n     INIT ContextParam: someParamYouWannaSet="+someParamYouWannaSet_);
      }
    }

    if(notFoundInit && notFoundContext )
    {
      /* Set the default values */
      //eagleDBDir_ = pEye_.getDerbyDBDir();
    }

    try
    {
      webServerHostName_ = InetAddress.getLocalHost().getHostName();
    }
    catch (UnknownHostException ex)
    {
      webServerHostName_ = "warp4.webarts.bc.ca";
    }
  }


  /**  Override to close Things up. **/
  public void destroy()
  {
    super.destroy();
  }


  /** Parses any defined servlet params into the defined class vars. **/
  private void parseParams(HttpServletRequest req)
  {
    ParameterParser parser = new ParameterParser(req);
    if(debugOut_)
    {
      System.out.println("\nRequest Params:");
      Enumeration<String> parmNames = req.getParameterNames();
      for (; parmNames.hasMoreElements();)
      {
        String currParamName = (String) parmNames.nextElement();
        System.out.println("  "+currParamName+"="+req.getParameter(currParamName));
      }
    }

    /* parse out the requeSystem.out.println("lastFmst params into class vars  */
    debugOut = parser.getStringParameter("debugOut", "");
    browserName = parser.getStringParameter("clientBrowser","");
    browserVersion = parser.getStringParameter("clientBrowserVersion","");
    filename = parser.getStringParameter("filename","");
    dirname = parser.getStringParameter("dirname","");

    if(filename.equals(""))
    {
      fileZip_=false;
      if(dirname.equals("")) validParms_=false;
    }

    if(debugOut_) { System.out.println("  filename="+filename+"   dirname="+dirname );}

   }


  /**
   *  Initisalizes File only if it is directory.It is represented by the passed
   *  in String.
   *
   * @param  s  the directory name to init as a File
   * @return    An instatiated File object if the passed string is a dir, null
   *      if not
   */
  private static File initDirFile(String s)
  {
    File retVal = null;
    File dirFile = new File(s);
    if (dirFile.isDirectory() && dirFile.canRead())
    {
      retVal = dirFile;
    }
    return retVal;
  }


  /**
   *  Takes a dirname and lists the contained files in an ArrayList for easier processing.
   *
   * @param  d  the directory name to scan for Files
   * @return    An instatiated ArrayList object full of the contained files or null if invalid.
   */
  private ArrayList <File> listDirFiles(String d)
  {
    ArrayList retVal = new ArrayList();;
    File dirToList = initDirFile(d);
    if (dirToList != null)
    {
      String[] files = dirToList.list();
      File tempFile;

      //loop through all the files in the dir
      for (int i = 0; i < files.length; i++)
      {
        tempFile = new File(d + File.separator + files[i]);
        if (tempFile.isDirectory())
        {
          // recurse on the subdirectory
          if(debugOut_) System.out.println("Adding DIR to list " + d + File.separator + files[i]);
          retVal.addAll(listDirFiles(d + File.separator + files[i]));
        }
        else
        {
          if(debugOut_) System.out.println("Adding to list " + d + File.separator + files[i]);
          retVal.add(new File(d + File.separator + files[i]));
        }
      }
    }
    else
    {
      if(d!=null && !d.equals(""))
      {
        if(debugOut_) System.out.println("Adding to list: " + d );
        retVal.add(new File(d));
      }
      else
        retVal = null;
    }
    return retVal;
  }


  /** Servlet get processing method. **/
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    parseParams(request);

    ServletContext context = getServletConfig().getServletContext();
    String cPath = context.getRealPath("/");

    if(validParms_)
    {

      String zipFilename = (fileZip_?
                                     filename.substring(0,
                                             (filename.lastIndexOf(".")>0?
                                                           filename.lastIndexOf("."):
                                                           filename.length()))+".zip"
                                    : dirname+".zip");

      ServletOutputStream out = response.getOutputStream();
      String conPath = request.getContextPath();

      if(debugOut_)
      {
        System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        System.out.println("ZipFiles.doGet   debugOut="+debugOut);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
      }

      // Set the content type based to zip
      response.setContentType("Content-type: text/zip");
      response.setHeader("Content-Disposition", "attachment; filename="+zipFilename);

      // List of files to be downloaded
      String fPath = cPath+File.separator+(dirname.equals("")?"":dirname+File.separator)+filename;
      ArrayList <File> filesList = listDirFiles(fPath);
      if(debugOut_) System.out.println("Searching for file in  " + fPath);

      if(filesList!=null)
      {

        ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(out));

        for (File file : filesList)
        {
          if(debugOut_) System.out.println("Adding to zip" + file.getName());
          zos.putNextEntry(new ZipEntry(file.getName()));

          // Get the file
          FileInputStream fis = null;
          try
          {
            fis = new FileInputStream(file);

          }
          catch (FileNotFoundException fnfe)
          {
            // If the file does not exists, write an error entry instead of
            // file
            // contents
            zos.write(("ERROR: could not find file " + file.getName()) .getBytes());
            zos.closeEntry();
            System.out.println("Could find file " + file.getAbsolutePath());
            continue;
          }

          BufferedInputStream fif = new BufferedInputStream(fis);

          // Write the contents of the file
          int data = 0;
          while ((data = fif.read()) != -1)
          {
            zos.write(data);
          }
          fif.close();

          zos.closeEntry();
          System.out.println("Finished zipping file " + file.getName());
        }

        zos.close();
      }
    }
  }
}



No one has commented yet.

Leave a Comment

HTML Syntax: NOT allowed