org.eclipse.jetty.util.MultiPartInputStream Maven / Gradle / Ivy
The newest version!
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.http.Part;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* MultiPartInputStream
*
* Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
*/
public class MultiPartInputStream
{
private static final Logger LOG = Log.getLogger(MultiPartInputStream.class);
public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
protected InputStream _in;
protected MultipartConfigElement _config;
protected String _contentType;
protected MultiMap _parts;
protected File _tmpDir;
protected File _contextTmpDir;
protected boolean _deleteOnExit;
public class MultiPart implements Part
{
protected String _name;
protected String _filename;
protected File _file;
protected OutputStream _out;
protected ByteArrayOutputStream2 _bout;
protected String _contentType;
protected MultiMap _headers;
protected long _size = 0;
protected boolean _temporary = true;
public MultiPart (String name, String filename)
throws IOException
{
_name = name;
_filename = filename;
}
protected void setContentType (String contentType)
{
_contentType = contentType;
}
protected void open()
throws IOException
{
//We will either be writing to a file, if it has a filename on the content-disposition
//and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
//will need to change to write to a file.
if (_filename != null && _filename.trim().length() > 0)
{
createFile();
}
else
{
//Write to a buffer in memory until we discover we've exceed the
//MultipartConfig fileSizeThreshold
_out = _bout= new ByteArrayOutputStream2();
}
}
protected void close()
throws IOException
{
_out.close();
}
protected void write (int b)
throws IOException
{
if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
createFile();
_out.write(b);
_size ++;
}
protected void write (byte[] bytes, int offset, int length)
throws IOException
{
if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
createFile();
_out.write(bytes, offset, length);
_size += length;
}
protected void createFile ()
throws IOException
{
final boolean USER = true;
final boolean WORLD = false;
_file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
_file.setReadable(false,WORLD); // (reset) disable it for everyone first
_file.setReadable(true,USER); // enable for user only
if (_deleteOnExit)
_file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
if (_size > 0 && _out != null)
{
//already written some bytes, so need to copy them into the file
_out.flush();
_bout.writeTo(bos);
_out.close();
_bout = null;
}
_out = bos;
}
protected void setHeaders(MultiMap headers)
{
_headers = headers;
}
/**
* @see javax.servlet.http.Part#getContentType()
*/
public String getContentType()
{
return _contentType;
}
/**
* @see javax.servlet.http.Part#getHeader(java.lang.String)
*/
public String getHeader(String name)
{
if (name == null)
return null;
return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
}
/**
* @see javax.servlet.http.Part#getHeaderNames()
*/
public Collection getHeaderNames()
{
return _headers.keySet();
}
/**
* @see javax.servlet.http.Part#getHeaders(java.lang.String)
*/
public Collection getHeaders(String name)
{
return _headers.getValues(name);
}
/**
* @see javax.servlet.http.Part#getInputStream()
*/
public InputStream getInputStream() throws IOException
{
if (_file != null)
{
//written to a file, whether temporary or not
return new BufferedInputStream (new FileInputStream(_file));
}
else
{
//part content is in memory
return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
}
}
public byte[] getBytes()
{
if (_bout!=null)
return _bout.toByteArray();
return null;
}
/**
* @see javax.servlet.http.Part#getName()
*/
public String getName()
{
return _name;
}
/**
* @see javax.servlet.http.Part#getSize()
*/
public long getSize()
{
return _size;
}
/**
* @see javax.servlet.http.Part#write(java.lang.String)
*/
public void write(String fileName) throws IOException
{
if (_file == null)
{
_temporary = false;
//part data is only in the ByteArrayOutputStream and never been written to disk
_file = new File (_tmpDir, fileName);
BufferedOutputStream bos = null;
try
{
bos = new BufferedOutputStream(new FileOutputStream(_file));
_bout.writeTo(bos);
bos.flush();
}
finally
{
if (bos != null)
bos.close();
_bout = null;
}
}
else
{
//the part data is already written to a temporary file, just rename it
_temporary = false;
File f = new File(_tmpDir, fileName);
if (_file.renameTo(f))
_file = f;
}
}
/**
* Remove the file, whether or not Part.write() was called on it
* (ie no longer temporary)
* @see javax.servlet.http.Part#delete()
*/
public void delete() throws IOException
{
if (_file != null && _file.exists())
_file.delete();
}
/**
* Only remove tmp files.
*
* @throws IOException
*/
public void cleanUp() throws IOException
{
if (_temporary && _file != null && _file.exists())
_file.delete();
}
/**
* Get the file, if any, the data has been written to.
* @return
*/
public File getFile ()
{
return _file;
}
/**
* Get the filename from the content-disposition.
* @return null or the filename
*/
public String getContentDispositionFilename ()
{
return _filename;
}
}
/**
* @param in Request input stream
* @param contentType Content-Type header
* @param config MultipartConfigElement
* @param contextTmpDir javax.servlet.context.tempdir
*/
public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
{
_in = new ReadLineInputStream(in);
_contentType = contentType;
_config = config;
_contextTmpDir = contextTmpDir;
if (_contextTmpDir == null)
_contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
if (_config == null)
_config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
}
/**
* Get the already parsed parts.
*
* @return
*/
public Collection getParsedParts()
{
if (_parts == null)
return Collections.emptyList();
Collection