DiskFileItemFactory.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.nio.charset.Charset;
  19. import java.nio.file.Path;

  20. import org.apache.commons.io.FileCleaningTracker;
  21. import org.apache.commons.io.build.AbstractOrigin;
  22. import org.apache.commons.io.build.AbstractStreamBuilder;
  23. import org.apache.commons.io.file.PathUtils;

  24. /**
  25.  * The default {@link FileItemFactory} implementation.
  26.  * <p>
  27.  * This implementation creates {@link FileItem} instances which keep their content either in memory, for smaller items, or in a temporary file on disk, for
  28.  * 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.
  29.  * </p>
  30.  * <p>
  31.  * If not otherwise configured, the default configuration values are as follows:
  32.  * </p>
  33.  * <ul>
  34.  * <li>Size threshold is 10 KB.</li>
  35.  * <li>Repository is the system default temporary directory, as returned by {@code System.getProperty("java.io.tmpdir")}.</li>
  36.  * </ul>
  37.  * <p>
  38.  * <strong>NOTE</strong>: Files are created in the system default temporary directory with predictable names. This means that a local attacker with write access
  39.  * to that 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
  40.  * the uploaded file is used but could be significant. When using this implementation in an environment with local, untrusted users,
  41.  * {@link Builder#setPath(Path)} MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified
  42.  * by the ServletContext attribute {@code javax.servlet.context.tempdir} may be used.
  43.  * </p>
  44.  * <p>
  45.  * 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
  46.  * set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically
  47.  * 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
  48.  * 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
  49.  * 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.
  50.  * </p>
  51.  *
  52.  * @see Builder
  53.  * @see Builder#get()
  54.  */
  55. public final class DiskFileItemFactory implements FileItemFactory<DiskFileItem> {

  56.     /**
  57.      * Builds a new {@link DiskFileItemFactory} instance.
  58.      * <p>
  59.      * For example:
  60.      * </p>
  61.      *
  62.      * <pre>{@code
  63.      * DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get();
  64.      * }
  65.      * </pre>
  66.      */
  67.     public static class Builder extends AbstractStreamBuilder<DiskFileItemFactory, Builder> {

  68.         /**
  69.          * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
  70.          * <p>
  71.          * May be null, if tracking files is not required.
  72.          * </p>
  73.          */
  74.         private FileCleaningTracker fileCleaningTracker;

  75.         /**
  76.          * Constructs a new instance.
  77.          */
  78.         public Builder() {
  79.             setBufferSize(DEFAULT_THRESHOLD);
  80.             setPath(PathUtils.getTempDirectory());
  81.             setCharset(DiskFileItem.DEFAULT_CHARSET);
  82.             setCharsetDefault(DiskFileItem.DEFAULT_CHARSET);
  83.         }

  84.         /**
  85.          * Constructs a new instance.
  86.          * <p>
  87.          * This builder use the aspects Path and buffer size.
  88.          * </p>
  89.          * <p>
  90.          * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an
  91.          * {@link UnsupportedOperationException}.
  92.          * </p>
  93.          *
  94.          * @return a new instance.
  95.          * @throws UnsupportedOperationException if the origin cannot provide a Path.
  96.          * @see AbstractOrigin#getReader(Charset)
  97.          */
  98.         @Override
  99.         public DiskFileItemFactory get() {
  100.             return new DiskFileItemFactory(getPath(), getBufferSize(), getCharset(), fileCleaningTracker);
  101.         }

  102.         /**
  103.          * Sets the tracker, which is responsible for deleting temporary files.
  104.          *
  105.          * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
  106.          * @return {@code this} instance.
  107.          */
  108.         public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) {
  109.             this.fileCleaningTracker = fileCleaningTracker;
  110.             return this;
  111.         }

  112.     }

  113.     /**
  114.      * The default threshold in bytes above which uploads will be stored on disk.
  115.      */
  116.     public static final int DEFAULT_THRESHOLD = 10_240;

  117.     /**
  118.      * Constructs a new {@link Builder}.
  119.      *
  120.      * @return a new {@link Builder}.
  121.      */
  122.     public static Builder builder() {
  123.         return new Builder();
  124.     }

  125.     /**
  126.      * The directory in which uploaded files will be stored, if stored on disk.
  127.      */
  128.     private final Path repository;

  129.     /**
  130.      * The threshold above which uploads will be stored on disk.
  131.      */
  132.     private final int threshold;

  133.     /**
  134.      * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files.
  135.      * <p>
  136.      * May be null, if tracking files is not required.
  137.      * </p>
  138.      */
  139.     private final FileCleaningTracker fileCleaningTracker;

  140.     /**
  141.      * Default content Charset to be used when no explicit Charset parameter is provided by the sender.
  142.      */
  143.     private final Charset charsetDefault;

  144.     /**
  145.      * Constructs a preconfigured instance of this class.
  146.      *
  147.      * @param repository          The data repository, which is the directory in which files will be created, should the item size exceed the threshold.
  148.      * @param threshold           The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.
  149.      * @param charsetDefault      Sets the default charset for use when no explicit charset parameter is provided by the sender.
  150.      * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking.
  151.      */
  152.     private DiskFileItemFactory(final Path repository, final int threshold, final Charset charsetDefault, final FileCleaningTracker fileCleaningTracker) {
  153.         this.threshold = threshold;
  154.         this.repository = repository;
  155.         this.charsetDefault = charsetDefault;
  156.         this.fileCleaningTracker = fileCleaningTracker;
  157.     }

  158.     @SuppressWarnings("unchecked")
  159.     @Override
  160.     public DiskFileItem.Builder fileItemBuilder() {
  161.         // @formatter:off
  162.         return DiskFileItem.builder()
  163.                 .setBufferSize(threshold)
  164.                 .setCharset(charsetDefault)
  165.                 .setFileCleaningTracker(fileCleaningTracker)
  166.                 .setPath(repository);
  167.         // @formatter:on
  168.     }

  169.     /**
  170.      * Gets the default charset for use when no explicit charset parameter is provided by the sender.
  171.      *
  172.      * @return the default charset
  173.      */
  174.     public Charset getCharsetDefault() {
  175.         return charsetDefault;
  176.     }

  177.     /**
  178.      * Gets the tracker, which is responsible for deleting temporary files.
  179.      *
  180.      * @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked.
  181.      */
  182.     public FileCleaningTracker getFileCleaningTracker() {
  183.         return fileCleaningTracker;
  184.     }

  185.     /**
  186.      * Gets the directory used to temporarily store files that are larger than the configured size threshold.
  187.      *
  188.      * @return The directory in which temporary files will be located.
  189.      */
  190.     public Path getRepository() {
  191.         return repository;
  192.     }

  193.     /**
  194.      * Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes.
  195.      *
  196.      * @return The size threshold in bytes.
  197.      */
  198.     public int getThreshold() {
  199.         return threshold;
  200.     }
  201. }