FileItemInputImpl.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.fileupload2.core;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.nio.file.InvalidPathException;

  21. import org.apache.commons.io.input.BoundedInputStream;

  22. /**
  23.  * Default implementation of {@link FileItemInput}.
  24.  */
  25. class FileItemInputImpl implements FileItemInput {

  26.     /**
  27.      * The File Item iterator implementation.
  28.      *
  29.      * @see FileItemInputIteratorImpl
  30.      */
  31.     private final FileItemInputIteratorImpl fileItemInputIteratorImpl;

  32.     /**
  33.      * The file items content type.
  34.      */
  35.     private final String contentType;

  36.     /**
  37.      * The file items field name.
  38.      */
  39.     private final String fieldName;

  40.     /**
  41.      * The file items file name.
  42.      */
  43.     private final String fileName;

  44.     /**
  45.      * Whether the file item is a form field.
  46.      */
  47.     private final boolean formField;

  48.     /**
  49.      * The file items input stream.
  50.      */
  51.     private final InputStream inputStream;

  52.     /**
  53.      * The file items input stream closed flag.
  54.      */
  55.     private boolean inputStreamClosed;

  56.     /**
  57.      * The headers, if any.
  58.      */
  59.     private FileItemHeaders headers;

  60.     /**
  61.      * Creates a new instance.
  62.      *
  63.      * @param fileItemIterator The {@link FileItemInputIteratorImpl iterator}, which returned this file item.
  64.      * @param fileName         The items file name, or null.
  65.      * @param fieldName        The items field name.
  66.      * @param contentType      The items content type, or null.
  67.      * @param formField        Whether the item is a form field.
  68.      * @param contentLength    The items content length, if known, or -1
  69.      * @throws IOException         Creating the file item failed.
  70.      * @throws FileUploadException Parsing the incoming data stream failed.
  71.      */
  72.     FileItemInputImpl(final FileItemInputIteratorImpl fileItemIterator, final String fileName, final String fieldName, final String contentType,
  73.             final boolean formField, final long contentLength) throws FileUploadException, IOException {
  74.         this.fileItemInputIteratorImpl = fileItemIterator;
  75.         this.fileName = fileName;
  76.         this.fieldName = fieldName;
  77.         this.contentType = contentType;
  78.         this.formField = formField;
  79.         final var fileSizeMax = fileItemInputIteratorImpl.getFileSizeMax();
  80.         if (fileSizeMax != -1 && contentLength != -1 && contentLength > fileSizeMax) {
  81.             throw new FileUploadByteCountLimitException(String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax),
  82.                     contentLength, fileSizeMax, fileName, fieldName);
  83.         }
  84.         // OK to construct stream now
  85.         final var itemInputStream = fileItemInputIteratorImpl.getMultiPartInput().newInputStream();
  86.         InputStream istream = itemInputStream;
  87.         if (fileSizeMax != -1) {
  88.             // onMaxLength will be called when the length is greater than _or equal to_ the supplied maxLength.
  89.             // Because we only want to throw an exception when the length is greater than fileSizeMax, we
  90.             // increment fileSizeMax by 1.
  91.             // @formatter:off
  92.             istream = BoundedInputStream.builder()
  93.                 .setInputStream(istream)
  94.                 .setMaxCount(fileSizeMax + 1)
  95.                 .setOnMaxCount((max, count) -> {
  96.                     itemInputStream.close(true);
  97.                     throw new FileUploadByteCountLimitException(
  98.                             String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax), count, fileSizeMax, fileName,
  99.                             fieldName);
  100.                 })
  101.                 .get();
  102.             // @formatter:on

  103.         }
  104.         this.inputStream = istream;
  105.     }

  106.     /**
  107.      * Closes the file item.
  108.      *
  109.      * @throws IOException An I/O error occurred.
  110.      */
  111.     public void close() throws IOException {
  112.         inputStream.close();
  113.         inputStreamClosed = true;
  114.     }

  115.     /**
  116.      * Gets the content type, or null.
  117.      *
  118.      * @return Content type, if known, or null.
  119.      */
  120.     @Override
  121.     public String getContentType() {
  122.         return contentType;
  123.     }

  124.     /**
  125.      * Gets the items field name.
  126.      *
  127.      * @return Field name.
  128.      */
  129.     @Override
  130.     public String getFieldName() {
  131.         return fieldName;
  132.     }

  133.     /**
  134.      * Gets the headers.
  135.      *
  136.      * @return The items header object
  137.      */
  138.     @Override
  139.     public FileItemHeaders getHeaders() {
  140.         return headers;
  141.     }

  142.     /**
  143.      * Gets the input stream, which may be used to read the items contents.
  144.      *
  145.      * @return Opened input stream.
  146.      * @throws IOException An I/O error occurred.
  147.      */
  148.     @Override
  149.     public InputStream getInputStream() throws IOException {
  150.         if (inputStreamClosed) {
  151.             throw new FileItemInput.ItemSkippedException("getInputStream()");
  152.         }
  153.         return inputStream;
  154.     }

  155.     /**
  156.      * Gets the file name.
  157.      *
  158.      * @return File name, if known, or null.
  159.      * @throws InvalidPathException The file name is invalid, for example it contains a NUL character, which might be an indicator of a security attack. If you
  160.      *                              intend to use the file name anyways, catch the exception and use InvalidPathException#getInput().
  161.      */
  162.     @Override
  163.     public String getName() {
  164.         return DiskFileItem.checkFileName(fileName);
  165.     }

  166.     /**
  167.      * Tests whether this is a form field.
  168.      *
  169.      * @return True, if the item is a form field, otherwise false.
  170.      */
  171.     @Override
  172.     public boolean isFormField() {
  173.         return formField;
  174.     }

  175.     /**
  176.      * Sets the file item headers.
  177.      *
  178.      * @param headers The items header object
  179.      */
  180.     @Override
  181.     public FileItemInputImpl setHeaders(final FileItemHeaders headers) {
  182.         this.headers = headers;
  183.         return this;
  184.     }

  185. }