Stephen Ramsay wrote:
On Sun, Sep 14, 2003 at 08:33:01AM -0400, Geoff Howard wrote:

Aha! That wiki example has a problem I just noticed -- you shouldn't return null here (I just corrected the wiki). Use the corrected version above - I'll explain below.

...

I'm not sure "return new Map()" will work in the revised Wiki
example.  Map is just an interface, so you can't actually return an
instantiated object.

DOH! Sorry, typing between commercials of the football game. I was undecided between two alternatives and took halfway between. Use this:

return java.util.Collections.EMPTY_MAP;

On the other hand, I'm not sure if returning some random object
(say, HashMap) is going to work.  I'm going to give it a try,
though, and see what happens.

Yes, that would work too. Well, not a random object but any instanceof Map obviously. As long as it's not null.

If I get this going, I'll try to put together an wikified example
that uses the Cocoon 2.1 API.

That'd be great. Actually, I have had a written but not tested version of a MultiPartFileAction on my hard drive for months which was meant to be committed into CVS but I haven't had the time or interest to finish up. If you're motivated to test it etc., it's attached. I was just finishing converting an original which merely saved to disk to now use ModifiableSource which would provide automatic support for other Source implementations, such as webdav repositories, cvs, etc. as they become available.

Geoff
/*

 ============================================================================
                   The Apache Software License, Version 1.1
 ============================================================================

 Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.

 Redistribution and use in source and binary forms, with or without modifica-
 tion, are permitted provided that the following conditions are met:

 1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

 3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

 4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    [EMAIL PROTECTED]

 5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
 DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
 OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
 ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
 (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

 This software  consists of voluntary contributions made  by many individuals
 on  behalf of the Apache Software  Foundation and was  originally created by
 Stefano Mazzocchi  <[EMAIL PROTECTED]>. For more  information on the Apache
 Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.acting;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;

import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.environment.Context;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.servlet.multipart.Part;
import org.apache.cocoon.servlet.multipart.PartOnDisk;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.ModifiableSource;
import org.apache.excalibur.source.impl.FileSource;

/**
 * Action to save (or move) uploaded file(s) in multipart requests to a 
 * configurable location.  Works with either PartOnDisk or PartInMemory 
 * (see web.xml and javadocs for configuration and definition).
 * 
 * Current implementation acts on all Part objects in the request 
 * and does not need/use the name of the html form element.  An enhancement 
 * would be to define an optional parameter for the object key to act on 
 * (the html form element's name attribute), falling back to the existing 
 * behavior when the parameter is absent.  This would a) allow multiple files 
 * in a single request to be handled individually, b) potentially provide a 
 * performance improvement where many request parameters are present.
 * 
 * Declaration: 
 * In map:components/map:actions section, define the action:
 * <code>&lt;map:action name="multipart" 
src="org.apache.cocoon.acting.MultiPartFileAction" 
logger="sitemap.acting.multipart"/&gt;</code>
 * 
 * Usage:
 * <pre>
 * <map:match pattern"myuploadurl">
 *     <map:act type="multipart">
 *       <map:parameter name="upload-dir" value="dir/to/save/to"/>
 *     </map:act>
 *     ...
 * </map:match>
 * </pre>
 * 
 * This action is originally based on code which appeared in the mail archives.
 * 
 * TODO: configure - one, list params, or all
 * TODO: custom max size?
 * TODO: location in config and param?
 * TODO: check that cleanup doesn't bomb if this moves them
 * 
 * 
 * @author <a href="mailto:[EMAIL PROTECTED]">Geoff Howard</a>
 * @version CVS $Id$
 */
public class MultiPartFileAction extends AbstractConfigurableAction implements 
ThreadSafe  {

  public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, 
        String source, Parameters parameters) throws Exception {  

      Request request = ObjectModelHelper.getRequest(objectModel);
      Context ctx = ObjectModelHelper.getContext(objectModel);
      
      Map results = Collections.EMPTY_MAP;
      String uploadDir=(String)parameters.getParameter("upload-dir");
      boolean debug = getLogger().isDebugEnabled();

      if (debug) {
        getLogger().debug("Using upload-dir: " + uploadDir);
      }
      
      Enumeration enum=request.getParameterNames();
      while(enum.hasMoreElements()) {
        String name=(String)enum.nextElement();
        Object obj=request.get(name);

        if (obj instanceof Part) {
            String fileName=((Part)obj).getFileName();
            //fileName = sanitizeFileName(fileName);  // Should be handled by 
MultipartParser
            if (debug) {
                getLogger().debug("Handling file: "+ fileName);
            }   
            
            //String canonicalName = ctx.getRealPath(uploadDir);
            
            Source outSource = resolver.resolveURI(uploadDir + fileName);
            if (outSource instanceof ModifiableSource) {
                if (debug) {
                    //getLogger().debug("Got Real Path: " + canonicalName);
                    getLogger().debug("Writing upload to source " + 
outSource.getURI());
                }
                try { 
                    saveUploadAs( (Part)obj , (ModifiableSource)outSource);
                } catch (Exception e) {
                    getLogger().error("Error Saving File: " + fileName);
                    getLogger().error(e.getMessage());
                    return null;
                }
            }
                        
        } else if (debug && obj instanceof String) {
            getLogger().debug("Skipping parameter: "+(String)obj);
        }           

      }

      return Collections.unmodifiableMap(results);

  }

     /**
      * Saves any <code>Part</code> object (eg, <code>PartOnDisk</code> 
      * or <code>PartInMemory</code>) to an arbitrary WritableSource.  
      * Legitimacy of newFile is assumed.
      *
      * @param fileData the Part object
      * @param source ModifiableSource
      *
      */
  private void saveUploadAs(Part fileData, ModifiableSource source) throws IOException 
{
    if (fileData instanceof PartOnDisk && source instanceof FileSource) {
        ((PartOnDisk)fileData).getFile().renameTo( ((FileSource)source).getFile() );
    } else {  
      byte[] buf=new byte[4096];
      OutputStream out = source.getOutputStream();
      InputStream in = null;
      try {
          in = ((Part) fileData).getInputStream();
      } catch (Exception e) {
          getLogger().error("Could not get InputStream from FilePart: " + 
              fileData.getFileName());
      }  
      int read=in.read(buf);
      while(read>0) {
          out.write(buf,0,read);
          read=in.read(buf);
      }
      out.close();
    }     
  }

   /**
    * Saves any <code>Part</code> object (eg, <code>PartOnDisk</code> 
    * or <code>PartInMemory</code>) to disk.  Legitimacy of newFile is assumed.
    *
    * @param fileData the FilePart object
    * @param newFile the new name/location
    * @deprecated
    */
  private void saveUploadAs(Part fileData, File newFile) throws IOException {
      if (fileData instanceof PartOnDisk) {
        ((PartOnDisk)fileData).getFile().renameTo(newFile);
      } else {  
        byte[] buf=new byte[4096];
        FileOutputStream out=new FileOutputStream(newFile);  
        InputStream in = null;
        try {
            in = ((Part) fileData).getInputStream();
        } catch (Exception e) {
            getLogger().error("Could not get InputStream from FilePart: " + 
                fileData.getFileName());
        }  
        int read=in.read(buf);
        while(read>0) {
            out.write(buf,0,read);
            read=in.read(buf);
        }
        out.close();
      }     
  }
}

Attachment: MultiPartFileAction.class
Description: Binary data

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to