All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.util.mime.MultipartMimeParser Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.util.mime;

import com.hfg.util.StringUtil;
import com.hfg.util.io.InputStreamSegment;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.*;

//------------------------------------------------------------------------------
/**
 Parser for multipart MIME.
 

See [RFC2045]

@author J. Alex Taylor, hairyfatguy.com */ //------------------------------------------------------------------------------ // com.hfg XML/HTML Coding Library // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ public class MultipartMimeParser { //########################################################################## // PRIVATE FIELDS //########################################################################## private String mContentType; private String mBoundary; private String mLineSeparator; private File mFileCacheDir; private boolean mEndIsHere = false; private static final String CONTENT_TYPE = "content-type:"; private static final String CONTENT_DISPOSITION = "content-disposition:"; private static Pattern sContentTypePattern = Pattern.compile("^\\s*(\\S+); boundary=(\\S+)$", Pattern.CASE_INSENSITIVE); //########################################################################## // PUBLIC METHODS //########################################################################## //-------------------------------------------------------------------------- public static boolean isMultipartMimeRequest(HttpServletRequest inRequest) throws IOException { String contentTypeHeader = inRequest.getHeader("Content-Type"); return (contentTypeHeader != null && sContentTypePattern.matcher(contentTypeHeader).matches()); } //-------------------------------------------------------------------------- public MultipartMimeParser setFileCacheDir(File inValue) { mFileCacheDir = inValue; return this; } //-------------------------------------------------------------------------- public List parse(HttpServletRequest inRequest) throws IOException { parseContentTypeFromHeader(inRequest); return parse(inRequest.getInputStream()); } //-------------------------------------------------------------------------- public List parse(InputStream inMimeStream) throws IOException { return parse(inMimeStream, null); } //-------------------------------------------------------------------------- public List parse(InputStream inMimeStream, String inBoundary) throws IOException { if (inBoundary != null) mBoundary = inBoundary; List entities = new ArrayList<>(); SpecialBufferedInputStream reader = null; try { reader = new SpecialBufferedInputStream(inMimeStream); String line; if (null == mBoundary) { do { line = reader.readLine(); } while (line != null && !line.toLowerCase().startsWith(CONTENT_TYPE)); if (null == line) { throw new MimeException("No content-type line found!"); } parseContentType(line.substring(line.indexOf(":"))); } // Read to the first boundary do { line = reader.readLine(); } while (line != null && !line.equals("--" + mBoundary)); if (null == line) { throw new MimeException("Problem detecting the initial MIME entity boundary!"); } MimeEntity entity; while ((entity = parseNextEntity(reader)) != null) { entities.add(entity); line = reader.readLine(); if (line.equals("--")) break; // Hit the last boundary. } } finally { if (reader != null) reader.close(); } return entities; } //########################################################################## // PRIVATE METHODS //########################################################################## //-------------------------------------------------------------------------- private void parseContentTypeFromHeader(HttpServletRequest inRequest) { parseContentType(inRequest.getHeader("Content-Type")); } //-------------------------------------------------------------------------- private void parseContentType(String inLine) { // Content-Type: multipart/form-data; boundary=---------------------------29772313742745 Matcher m = sContentTypePattern.matcher(inLine); if (m.matches()) { mContentType = m.group(1); mBoundary = m.group(2); } else { throw new MimeException("Failed to parse the content-type line '" + inLine + "'!"); } } //-------------------------------------------------------------------------- private MimeEntity parseNextEntity(SpecialBufferedInputStream inReader) throws IOException { MimeEntity entity = new MimeEntity(); // Read the entity's content-disposition readEntityHeader(entity, inReader); // Read the entity's content InputStreamSegment contentStream = new InputStreamSegment(inReader, getEntityEndMarker().getBytes()); if (mFileCacheDir != null && entity.getContentDisposition().getFilename() != null) { String filename = entity.getContentDisposition().getFilename().trim(); File file = new File(mFileCacheDir, filename); if (! mFileCacheDir.exists()) { if (! mFileCacheDir.mkdirs()) { throw new IOException("Couldn't create dir " + StringUtil.singleQuote(mFileCacheDir.getPath()) + " as user " + System.getProperty("user.name") + "!"); } } entity.setCachedContentFile(file); streamSegmentToFile(contentStream, file); } else { entity.setContent(streamSegmentToString(contentStream)); } return entity; } //--------------------------------------------------------------------------- private void readEntityHeader(MimeEntity inEntity, SpecialBufferedInputStream inReader) throws IOException { String line = null; while ((line = inReader.readLine()) != null && line.length() > 0) { if (line.toLowerCase().startsWith(CONTENT_TYPE)) { inEntity.setContentType(parseEntityContentTypeLine(line)); } else if (line.toLowerCase().startsWith(CONTENT_DISPOSITION)) { inEntity.setContentDisposition(parseEntityContentDispositionLine(line)); } } if (null == inEntity.getContentDisposition()) { throw new MimeException("Mime entity without a Content-Disposition detected!"); } } //-------------------------------------------------------------------------- private String parseEntityContentTypeLine(String inLine) { // Content-Type: image/png String contentType; Pattern pattern = Pattern.compile("^\\s*Content-Type: (\\S+)$", Pattern.CASE_INSENSITIVE); Matcher m = pattern.matcher(inLine); if (m.matches()) { contentType = m.group(1); } else { throw new MimeException("Failed to parse the entity content-type line '" + inLine + "'!"); } return contentType; } //-------------------------------------------------------------------------- private MimeContentDisposition parseEntityContentDispositionLine(String inLine) { // Content-Disposition: form-data; name="action" // Content-Disposition: form-data; name="upload_file"; filename="rss.png" MimeContentDisposition contentDisposition = new MimeContentDisposition(); Pattern pattern = Pattern.compile("^\\s*Content-Disposition: (\\S+); name=\"([^\"]+)\"(?:; filename=\"([^\"]+)\")?$", Pattern.CASE_INSENSITIVE); Matcher m = pattern.matcher(inLine); if (m.matches()) { contentDisposition.setType(m.group(1)); contentDisposition.setName(m.group(2)); if (m.group(3) != null) { contentDisposition.setFilename(m.group(3)); } } else { throw new MimeException("Failed to parse the entity content-disposition line '" + inLine + "'!"); } return contentDisposition; } //--------------------------------------------------------------------------- private String getEntityEndMarker() { return mLineSeparator + "--" + mBoundary; } //--------------------------------------------------------------------------- /** Reads the contents of the specified InputStreamSegment into a String. */ private String streamSegmentToString(InputStreamSegment inStream) throws IOException { StringBuilder buffer = new StringBuilder(); byte[] bytes = new byte[1024]; int bytesRead = 0; while ((bytesRead = inStream.read(bytes)) != -1) { buffer.append(new String(bytes, 0, bytesRead)); } return buffer.toString(); } //--------------------------------------------------------------------------- /** Reads the contents of the specified InputStreamSegment into a File. */ private void streamSegmentToFile(InputStreamSegment inStream, File inFile) throws IOException { OutputStream fileStream = null; try { fileStream = new BufferedOutputStream(new FileOutputStream(inFile)); byte[] bytes = new byte[1024]; int bytesRead = 0; while ((bytesRead = inStream.read(bytes)) != -1) { fileStream.write(bytes, 0, bytesRead); } } finally { if (fileStream != null) fileStream.close(); } } //########################################################################## // INNER CLASS //########################################################################## private class SpecialBufferedInputStream extends BufferedInputStream { //----------------------------------------------------------------------- public SpecialBufferedInputStream(InputStream in) { super(in); } //----------------------------------------------------------------------- public String readLine() throws IOException { StringBuilder line = new StringBuilder(); if (!mEndIsHere) { do { int theChar = read(); if (-1 == theChar) { mEndIsHere = true; break; } if (theChar != '\n' && theChar != '\r') { line.append((char)theChar); } else { if (null == mLineSeparator) mLineSeparator = (char) theChar + ""; if (mLineSeparator.equals("\r\n") || (theChar == '\r' && pos < count && buf[pos] == '\n')) { read(); if (mLineSeparator.length() == 1) mLineSeparator = "\r\n"; } break; } } while (true); } return (mEndIsHere && 0 == line.length() ? null : line.toString()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy