View Javadoc

    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
    19  import java.io.IOException;
    20  import java.io.InputStream;
    21  import java.nio.file.InvalidPathException;
    22
    23  import org.apache.commons.io.input.BoundedInputStream;
    24
    25  /**
    26   * Default implementation of {@link FileItemInput}.
    27   */
    28  class FileItemInputImpl implements FileItemInput {
    29
    30      /**
    31       * The File Item iterator implementation.
    32       *
    33       * @see FileItemInputIteratorImpl
    34       */
    35      private final FileItemInputIteratorImpl fileItemInputIteratorImpl;
    36
    37      /**
    38       * The file items content type.
    39       */
    40      private final String contentType;
    41
    42      /**
    43       * The file items field name.
    44       */
    45      private final String fieldName;
    46
    47      /**
    48       * The file items file name.
    49       */
    50      private final String fileName;
    51
    52      /**
    53       * Whether the file item is a form field.
    54       */
    55      private final boolean formField;
    56
    57      /**
    58       * The file items input stream.
    59       */
    60      private final InputStream inputStream;
    61
    62      /**
    63       * The file items input stream closed flag.
    64       */
    65      private boolean inputStreamClosed;
    66
    67      /**
    68       * The headers, if any.
    69       */
    70      private FileItemHeaders headers;
    71
    72      /**
    73       * Creates a new instance.
    74       *
    75       * @param fileItemIterator The {@link FileItemInputIteratorImpl iterator}, which returned this file item.
    76       * @param fileName         The items file name, or null.
    77       * @param fieldName        The items field name.
    78       * @param contentType      The items content type, or null.
    79       * @param formField        Whether the item is a form field.
    80       * @param contentLength    The items content length, if known, or -1
    81       * @throws IOException         Creating the file item failed.
    82       * @throws FileUploadException Parsing the incoming data stream failed.
    83       */
    84      FileItemInputImpl(final FileItemInputIteratorImpl fileItemIterator, final String fileName, final String fieldName, final String contentType,
    85              final boolean formField, final long contentLength) throws FileUploadException, IOException {
    86          this.fileItemInputIteratorImpl = fileItemIterator;
    87          this.fileName = fileName;
    88          this.fieldName = fieldName;
    89          this.contentType = contentType;
    90          this.formField = formField;
    91          final var fileSizeMax = fileItemInputIteratorImpl.getFileSizeMax();
    92          if (fileSizeMax != -1 && contentLength != -1 && contentLength > fileSizeMax) {
    93              throw new FileUploadByteCountLimitException(String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax),
    94                      contentLength, fileSizeMax, fileName, fieldName);
    95          }
    96          // OK to construct stream now
    97          final var itemInputStream = fileItemInputIteratorImpl.getMultiPartInput().newInputStream();
    98          InputStream istream = itemInputStream;
    99          if (fileSizeMax != -1) {
    100             // onMaxLength will be called when the length is greater than _or equal to_ the supplied maxLength.
    101             // Because we only want to throw an exception when the length is greater than fileSizeMax, we
    102             // increment fileSizeMax by 1.
    103             istream = new BoundedInputStream(istream, fileSizeMax + 1) {
    104                 @Override
    105                 protected void onMaxLength(final long sizeMax, final long count) throws IOException {
    106                     itemInputStream.close(true);
    107                     throw new FileUploadByteCountLimitException(
    108                             String.format("The field %s exceeds its maximum permitted size of %s bytes.", fieldName, fileSizeMax), count, fileSizeMax, fileName,
    109                             fieldName);
    110                 }
    111             };
    112         }
    113         this.inputStream = istream;
    114     }
    115
    116     /**
    117      * Closes the file item.
    118      *
    119      * @throws IOException An I/O error occurred.
    120      */
    121     public void close() throws IOException {
    122         inputStream.close();
    123         inputStreamClosed = true;
    124     }
    125
    126     /**
    127      * Gets the content type, or null.
    128      *
    129      * @return Content type, if known, or null.
    130      */
    131     @Override
    132     public String getContentType() {
    133         return contentType;
    134     }
    135
    136     /**
    137      * Gets the items field name.
    138      *
    139      * @return Field name.
    140      */
    141     @Override
    142     public String getFieldName() {
    143         return fieldName;
    144     }
    145
    146     /**
    147      * Gets the headers.
    148      *
    149      * @return The items header object
    150      */
    151     @Override
    152     public FileItemHeaders getHeaders() {
    153         return headers;
    154     }
    155
    156     /**
    157      * Gets the input stream, which may be used to read the items contents.
    158      *
    159      * @return Opened input stream.
    160      * @throws IOException An I/O error occurred.
    161      */
    162     @Override
    163     public InputStream getInputStream() throws IOException {
    164         if (inputStreamClosed) {
    165             throw new FileItemInput.ItemSkippedException("getInputStream()");
    166         }
    167         return inputStream;
    168     }
    169
    170     /**
    171      * Gets the file name.
    172      *
    173      * @return File name, if known, or null.
    174      * @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
    175      *                              intend to use the file name anyways, catch the exception and use InvalidPathException#getInput().
    176      */
    177     @Override
    178     public String getName() {
    179         return DiskFileItem.checkFileName(fileName);
    180     }
    181
    182     /**
    183      * Tests whether this is a form field.
    184      *
    185      * @return True, if the item is a form field, otherwise false.
    186      */
    187     @Override
    188     public boolean isFormField() {
    189         return formField;
    190     }
    191
    192     /**
    193      * Sets the file item headers.
    194      *
    195      * @param headers The items header object
    196      */
    197     @Override
    198     public FileItemInputImpl setHeaders(final FileItemHeaders headers) {
    199         this.headers = headers;
    200         return this;
    201     }
    202
    203 }