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.nio.charset.Charset;
    20  import java.nio.file.Path;
    21
    22  import org.apache.commons.io.FileCleaningTracker;
    23  import org.apache.commons.io.build.AbstractOrigin;
    24  import org.apache.commons.io.build.AbstractStreamBuilder;
    25  import org.apache.commons.io.file.PathUtils;
    26
    27  /**
    28   * The default {@link FileItemFactory} implementation.
    29   * <p>
    30   * This implementation creates {@link FileItem} instances which keep their content either in memory, for smaller items, or in a temporary file on disk, for
    31   * larger items. The size threshold, above which content will be stored on disk, is configurable, as is the directory in which temporary files will be created.
    32   * </p>
    33   * <p>
    34   * If not otherwise configured, the default configuration values are as follows:
    35   * </p>
    36   * <ul>
    37   * <li>Size threshold is 10 KB.</li>
    38   * <li>Repository is the system default temporary directory, as returned by {@code System.getProperty("java.io.tmpdir")}.</li>
    39   * </ul>
    40   * <p>
    41   * <b>NOTE</b>: Files are created in the system default temporary directory with predictable names. This means that a local attacker with write access to that
    42   * directory can perform a TOUTOC attack to replace any uploaded file with a file of the attackers choice. The implications of this will depend on how the
    43   * uploaded file is used but could be significant. When using this implementation in an environment with local, untrusted users, {@link Builder#setPath(Path)}
    44   * MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified by the ServletContext attribute
    45   * {@code javax.servlet.context.tempdir} may be used.
    46   * </p>
    47   * <p>
    48   * Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a {@link FileCleaningTracker}, which you can
    49   * set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically
    50   * deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of {@link java.io.File} is garbage collected.) This is done by
    51   * the so-called reaper thread, which is started and stopped automatically by the {@link FileCleaningTracker} when there are files to be tracked. It might make
    52   * sense to terminate that thread, for example, if your web application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.
    53   * </p>
    54   *
    55   * @see Builder
    56   * @see Builder#get()
    57   */
    58  public final class DiskFileItemFactory implements FileItemFactory<DiskFileItem> {
    59
    60      /**
    61       * Builds a new {@link DiskFileItemFactory} instance.
    62       * <p>
    63       * For example:
    64       * </p>
    65       *
    66       * <pre>{@code
    67       * DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get();
    68       * }
    69       * </pre>
    70       */
    71      public static class Builder extends AbstractStreamBuilder<DiskFileItemFactory, Builder> {
    72
    73          /**
    74           * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
    75           * <p>
    76           * May be null, if tracking files is not required.
    77           * </p>
    78           */
    79          private FileCleaningTracker fileCleaningTracker;
    80
    81          public Builder() {
    82              setBufferSize(DEFAULT_THRESHOLD);
    83              setPath(PathUtils.getTempDirectory());
    84              setCharset(DiskFileItem.DEFAULT_CHARSET);
    85              setCharsetDefault(DiskFileItem.DEFAULT_CHARSET);
    86          }
    87
    88          /**
    89           * Constructs a new instance.
    90           * <p>
    91           * This builder use the aspects Path and buffer size.
    92           * </p>
    93           * <p>
    94           * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
    95           * {@link UnsupportedOperationException}.
    96           * </p>
    97           *
    98           * @return a new instance.
    99           * @throws UnsupportedOperationException if the origin cannot provide a Path.
    100          * @see AbstractOrigin#getReader(Charset)
    101          */
    102         @Override
    103         public DiskFileItemFactory get() {
    104             return new DiskFileItemFactory(getPath(), getBufferSize(), getCharset(), fileCleaningTracker);
    105         }
    106
    107         /**
    108          * Sets the tracker, which is responsible for deleting temporary files.
    109          *
    110          * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
    111          * @return this
    112          */
    113         public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
    114             this.fileCleaningTracker = fileCleaningTracker;
    115             return this;
    116         }
    117
    118     }
    119
    120     /**
    121      * The default threshold in bytes above which uploads will be stored on disk.
    122      */
    123     public static final int DEFAULT_THRESHOLD = 10_240;
    124
    125     /**
    126      * Constructs a new {@link Builder}.
    127      *
    128      * @return a new {@link Builder}.
    129      */
    130     public static Builder builder() {
    131         return new Builder();
    132     }
    133
    134     /**
    135      * The directory in which uploaded files will be stored, if stored on disk.
    136      */
    137     private final Path repository;
    138
    139     /**
    140      * The threshold above which uploads will be stored on disk.
    141      */
    142     private final int threshold;
    143
    144     /**
    145      * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
    146      * <p>
    147      * May be null, if tracking files is not required.
    148      * </p>
    149      */
    150     private final FileCleaningTracker fileCleaningTracker;
    151
    152     /**
    153      * Default content Charset to be used when no explicit Charset parameter is provided by the sender.
    154      */
    155     private final Charset charsetDefault;
    156
    157     /**
    158      * Constructs a preconfigured instance of this class.
    159      *
    160      * @param repository          The data repository, which is the directory in which files will be created, should the item size exceed the threshold.
    161      * @param threshold           The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.
    162      * @param charsetDefault      Sets the default charset for use when no explicit charset parameter is provided by the sender.
    163      * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
    164      */
    165     private DiskFileItemFactory(final Path repository, final int threshold, final Charset charsetDefault, final FileCleaningTracker fileCleaningTracker) {
    166         this.threshold = threshold;
    167         this.repository = repository;
    168         this.charsetDefault = charsetDefault;
    169         this.fileCleaningTracker = fileCleaningTracker;
    170     }
    171
    172     @SuppressWarnings("unchecked")
    173     @Override
    174     public DiskFileItem.Builder fileItemBuilder() {
    175         // @formatter:off
    176         return DiskFileItem.builder()
    177                 .setBufferSize(threshold)
    178                 .setCharset(charsetDefault)
    179                 .setFileCleaningTracker(fileCleaningTracker)
    180                 .setPath(repository);
    181         // @formatter:on
    182     }
    183
    184     /**
    185      * Gets the default charset for use when no explicit charset parameter is provided by the sender.
    186      *
    187      * @return the default charset
    188      */
    189     public Charset getCharsetDefault() {
    190         return charsetDefault;
    191     }
    192
    193     /**
    194      * Gets the tracker, which is responsible for deleting temporary files.
    195      *
    196      * @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked.
    197      */
    198     public FileCleaningTracker getFileCleaningTracker() {
    199         return fileCleaningTracker;
    200     }
    201
    202     /**
    203      * Gets the directory used to temporarily store files that are larger than the configured size threshold.
    204      *
    205      * @return The directory in which temporary files will be located.
    206      */
    207     public Path getRepository() {
    208         return repository;
    209     }
    210
    211     /**
    212      * Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes.
    213      *
    214      * @return The size threshold in bytes.
    215      */
    216     public int getThreshold() {
    217         return threshold;
    218     }
    219 }