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 }