001/*
002 *  $HeadURL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/servlet/ZipFiles.java $
003 *  $Revision: 1105 $
004 *  $LastChangedDate: 2016-05-16 19:19:44 -0700 (Mon, 16 May 2016) $
005 *  $LastChangedBy: tgutwin $
006 *  Copyright (c) 2016 Tom B. Gutwin P.Eng. North Vancouver BC Canada
007 *
008 *  This program is free software; you can redistribute it and/or
009 *  modify it under the terms of the GNU General Public License
010 *  as published by the Free Software Foundation; either version 3
011 *  of the License, or any later version.
012 *
013 *  This program is distributed in the hope that it will be useful,
014 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
015 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 *  GNU General Public License for more details.
017 *
018 *  You should have received a copy of the GNU General Public License
019 *  along with this program; If not, see <http://www.gnu.org/licenses/>.
020 */
021package ca.bc.webarts.servlet;
022
023import java.io.BufferedInputStream;
024import java.io.BufferedOutputStream;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.FileNotFoundException;
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.Enumeration;
031import java.util.List;
032import java.net.InetAddress;
033import java.net.UnknownHostException;
034import java.util.zip.ZipEntry;
035import java.util.zip.ZipOutputStream;
036
037import javax.servlet.ServletConfig;
038import javax.servlet.ServletContext;
039import javax.servlet.ServletException;
040import javax.servlet.ServletOutputStream;
041import javax.servlet.http.HttpServlet;
042import javax.servlet.http.HttpServletRequest;
043import javax.servlet.http.HttpServletResponse;
044
045import com.oreilly.servlet.ParameterParser;
046import com.oreilly.servlet.ParameterNotFoundException;
047import com.oreilly.servlet.ServletUtils;
048
049
050/**
051 * <h1>Downloading single or Multiple Files As Zip file.</h1>
052 *
053 * <h2>3 steps to implement</h2><ol><li>add this class to WEB-INF/classes</li>
054 * <li>add the following to your web.xml file<pre>
055  &lt;servlet&gt;
056    &lt;servlet-name&gt;ZipFiles&lt;/servlet-name&gt;
057       &lt;servlet-class&gt;ca.bc.webarts.servlet.ZipFiles&lt;/servlet-class&gt;
058    &lt;/servlet&gt;
059    &lt;servlet-mapping&gt;
060      &lt;servlet-name&gt;ZipFiles&lt;/servlet-name&gt;
061      &lt;url-pattern&gt;/zipfiles/*&lt;/url-pattern&gt;
062    &lt;/servlet-mapping&gt;
063  &lt;/servlet&gt;
064 </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)
065 * 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>
066 * <li> example: http://warp4.webarts.bc.ca/tunes/ZipFiles/?filename=AThousandSuns_03-Radiance.ogg&dirname=mythTunes/LinkinPark/AThousandSuns</li>
067 * <li> NOTE: filename param is OPTIONAL. if NOT provided, this servlet will zip all files in the dirname.</li>
068 * </ul>
069 * </li>
070 * </ol>
071 * <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>
072 * <a href="http://www.gnu.org/licenses/licenses.html#GPL">General Public License (GNU GPL)</a>.</h4>
073 */
074public class ZipFiles extends HttpServlet
075{
076
077  private static final long serialVersionUID = -2767248383788052391L;
078  /** Class constant. **/
079  protected static final String className_ = "ZipFiles";
080  private final static String SYSTEM_FILE_SEPERATOR = File.separator;
081  /**  Version String.  */
082  private final static String SERVLET_VERSION = "0.01_[$Rev: 1105 $]";
083
084  /**  Build String. (yymmddhhss)  */
085  private final static String BUILD_TAG = "161015143718";
086
087  private static String webServerHostName_ = "warp4.webarts.bc.ca";
088
089  private boolean debugOut_ = false;
090  /** flag if the parms have specified a file (true) or a dir (false) to zip. **/
091  private boolean fileZip_ = true;
092  /** flag if the parms are valid. **/
093  private boolean validParms_ = true;
094
095  /**  controls if extra info is dumped to system.out. **/
096  private String debugOut = "";
097  /** not used. **/
098  private String browserName = "";
099  /** not used. **/
100  private String browserVersion = "";
101
102  /** Servlet parameter to specify the specific file(name) to zip up and return. **/
103  private String filename = "";
104  /** 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. **/
105  private String dirname = "";
106  private String someParamYouWannaSet_ = "";
107
108  /**
109   *  Gets the ServletInfo attribute of this servlet
110   *
111   * @return    The ServletInfo value
112   */
113  public String getServletInfo()
114  {
115    final String methodName = "getServletInfo";
116    return "WebARTS Design ZipFiles servlet. Version:" + SERVLET_VERSION +
117        "  Build:" + BUILD_TAG;
118  }
119
120
121  /**
122   * The one time servlet init stuff goes here. It sets the derbyDBDir based on the following prioritized varables:
123   * <ol><li>context init param: derbyDBDir</li><li>servlet init param (from web.xml): derbyDBDir</li>
124   * <li>default hardcoded variable: derbyDBDir</li></ol>If defined in multiple places, the higher priority item will
125   * be used.
126   **/
127  public void init()
128  {
129    System.out.println("\n~~~~~~~~\n~~~~~~~~\nInitializing ca.bc.webarts.servlet.ZipFiles\n~~~~~~~~\n~~~~~~~~");
130
131    boolean notFoundInit = true;
132    boolean notFoundContext = true;
133    java.util.Enumeration <String> initEnum = getInitParameterNames();
134    for (; notFoundInit && initEnum.hasMoreElements();)
135    {
136      if(initEnum.nextElement().equals("someParamYouWannaSet"))
137      {
138        notFoundInit = false;
139        /* Do something with the init param */
140        someParamYouWannaSet_ = getInitParameter("someParamYouWannaSet");
141        System.out.println("\n~~~~~~~~\n     INIT ServletParam: someParamYouWannaSet="+someParamYouWannaSet_);
142      }
143    }
144
145    //also check context
146    initEnum = getServletConfig().getServletContext().getInitParameterNames();
147    for (; notFoundContext && initEnum.hasMoreElements();)
148    {
149      if(initEnum.nextElement().equals("someParamYouWannaSet"))
150      {
151        notFoundContext = false;
152        /* Do something with the init param */
153        someParamYouWannaSet_ = getServletConfig().getServletContext().getInitParameter("baseDir");
154        System.out.println("\n~~~~~~~~\n     INIT ContextParam: someParamYouWannaSet="+someParamYouWannaSet_);
155      }
156    }
157
158    if(notFoundInit && notFoundContext )
159    {
160      /* Set the default values */
161      //eagleDBDir_ = pEye_.getDerbyDBDir();
162    }
163
164    try
165    {
166      webServerHostName_ = InetAddress.getLocalHost().getHostName();
167    }
168    catch (UnknownHostException ex)
169    {
170      webServerHostName_ = "warp4.webarts.bc.ca";
171    }
172  }
173
174
175  /**  Override to close Things up. **/
176  public void destroy()
177  {
178    super.destroy();
179  }
180
181
182  /** Parses any defined servlet params into the defined class vars. **/
183  private void parseParams(HttpServletRequest req)
184  {
185    ParameterParser parser = new ParameterParser(req);
186    if(debugOut_)
187    {
188      System.out.println("\nRequest Params:");
189      Enumeration<String> parmNames = req.getParameterNames();
190      for (; parmNames.hasMoreElements();)
191      {
192        String currParamName = (String) parmNames.nextElement();
193        System.out.println("  "+currParamName+"="+req.getParameter(currParamName));
194      }
195    }
196
197    /* parse out the requeSystem.out.println("lastFmst params into class vars  */
198    debugOut = parser.getStringParameter("debugOut", "");
199    browserName = parser.getStringParameter("clientBrowser","");
200    browserVersion = parser.getStringParameter("clientBrowserVersion","");
201    filename = parser.getStringParameter("filename","");
202    dirname = parser.getStringParameter("dirname","");
203
204    if(filename.equals(""))
205    {
206      fileZip_=false;
207      if(dirname.equals("")) validParms_=false;
208    }
209
210    if(debugOut_) { System.out.println("  filename="+filename+"   dirname="+dirname );}
211
212   }
213
214
215  /**
216   *  Initisalizes File only if it is directory.It is represented by the passed
217   *  in String.
218   *
219   * @param  s  the directory name to init as a File
220   * @return    An instatiated File object if the passed string is a dir, null
221   *      if not
222   */
223  private static File initDirFile(String s)
224  {
225    File retVal = null;
226    File dirFile = new File(s);
227    if (dirFile.isDirectory() && dirFile.canRead())
228    {
229      retVal = dirFile;
230    }
231    return retVal;
232  }
233
234
235  /**
236   *  Takes a dirname and lists the contained files in an ArrayList for easier processing.
237   *
238   * @param  d  the directory name to scan for Files
239   * @return    An instatiated ArrayList object full of the contained files or null if invalid.
240   */
241  private ArrayList <File> listDirFiles(String d)
242  {
243    ArrayList retVal = new ArrayList();;
244    File dirToList = initDirFile(d);
245    if (dirToList != null)
246    {
247      String[] files = dirToList.list();
248      File tempFile;
249
250      //loop through all the files in the dir
251      for (int i = 0; i < files.length; i++)
252      {
253        tempFile = new File(d + File.separator + files[i]);
254        if (tempFile.isDirectory())
255        {
256          // recurse on the subdirectory
257          if(debugOut_) System.out.println("Adding DIR to list " + d + File.separator + files[i]);
258          retVal.addAll(listDirFiles(d + File.separator + files[i]));
259        }
260        else
261        {
262          if(debugOut_) System.out.println("Adding to list " + d + File.separator + files[i]);
263          retVal.add(new File(d + File.separator + files[i]));
264        }
265      }
266    }
267    else
268    {
269      if(d!=null && !d.equals(""))
270      {
271        if(debugOut_) System.out.println("Adding to list: " + d );
272        retVal.add(new File(d));
273      }
274      else
275        retVal = null;
276    }
277    return retVal;
278  }
279
280
281  /** Servlet get processing method. **/
282  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
283  {
284    parseParams(request);
285
286    ServletContext context = getServletConfig().getServletContext();
287    String cPath = context.getRealPath("/");
288
289    if(validParms_)
290    {
291
292      String zipFilename = (fileZip_?
293                                     filename.substring(0,
294                                             (filename.lastIndexOf(".")>0?
295                                                           filename.lastIndexOf("."):
296                                                           filename.length()))+".zip"
297                                    : dirname+".zip");
298
299      ServletOutputStream out = response.getOutputStream();
300      String conPath = request.getContextPath();
301
302      if(debugOut_)
303      {
304        System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~");
305        System.out.println("ZipFiles.doGet   debugOut="+debugOut);
306        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
307      }
308
309      // Set the content type based to zip
310      response.setContentType("Content-type: text/zip");
311      response.setHeader("Content-Disposition", "attachment; filename="+zipFilename);
312
313      // List of files to be downloaded
314      String fPath = cPath+File.separator+(dirname.equals("")?"":dirname+File.separator)+filename;
315      ArrayList <File> filesList = listDirFiles(fPath);
316      if(debugOut_) System.out.println("Searching for file in  " + fPath);
317
318      if(filesList!=null)
319      {
320
321        ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(out));
322
323        for (File file : filesList)
324        {
325          if(debugOut_) System.out.println("Adding to zip" + file.getName());
326          zos.putNextEntry(new ZipEntry(file.getName()));
327
328          // Get the file
329          FileInputStream fis = null;
330          try
331          {
332            fis = new FileInputStream(file);
333
334          }
335          catch (FileNotFoundException fnfe)
336          {
337            // If the file does not exists, write an error entry instead of
338            // file
339            // contents
340            zos.write(("ERROR: could not find file " + file.getName()) .getBytes());
341            zos.closeEntry();
342            System.out.println("Could find file " + file.getAbsolutePath());
343            continue;
344          }
345
346          BufferedInputStream fif = new BufferedInputStream(fis);
347
348          // Write the contents of the file
349          int data = 0;
350          while ((data = fif.read()) != -1)
351          {
352            zos.write(data);
353          }
354          fif.close();
355
356          zos.closeEntry();
357          System.out.println("Finished zipping file " + file.getName());
358        }
359
360        zos.close();
361      }
362    }
363  }
364}