001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.fileupload2.core; 018 019import java.nio.charset.Charset; 020import java.nio.file.Path; 021 022import org.apache.commons.io.FileCleaningTracker; 023import org.apache.commons.io.build.AbstractOrigin; 024import org.apache.commons.io.build.AbstractStreamBuilder; 025import org.apache.commons.io.file.PathUtils; 026 027/** 028 * The default {@link FileItemFactory} implementation. 029 * <p> 030 * This implementation creates {@link FileItem} instances which keep their content either in memory, for smaller items, or in a temporary file on disk, for 031 * 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. 032 * </p> 033 * <p> 034 * If not otherwise configured, the default configuration values are as follows: 035 * </p> 036 * <ul> 037 * <li>Size threshold is 10 KB.</li> 038 * <li>Repository is the system default temporary directory, as returned by {@code System.getProperty("java.io.tmpdir")}.</li> 039 * </ul> 040 * <p><em>State model</em>: The created instances of {@link DiskFileItem} are subject to a carefully designed state model, 041 * which is also controlled by the threshold. Therefore, it is strongly recommended to set the threshold explicitly, using 042 * {@link Builder#setThreshold(int)}. Details 043 * on the state model can be found {@link DiskFileItem here}.</p> 044 * <p> 045 * <strong>NOTE</strong>: Files are created in the system default temporary directory with predictable names. This means that a local attacker with write access 046 * 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 047 * the uploaded file is used, but could be significant. When using this implementation in an environment with local, untrusted users, 048 * {@link Builder#setPath(Path)} MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified 049 * by the ServletContext attribute {@code javax.servlet.context.tempdir} may be used. 050 * </p> 051 * <p> 052 * 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 053 * set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically 054 * 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 055 * 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 056 * 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. 057 * </p> 058 * 059 * @see Builder 060 * @see Builder#get() 061 */ 062public final class DiskFileItemFactory implements FileItemFactory<DiskFileItem> { 063 064 /** 065 * Builds a new {@link DiskFileItemFactory} instance. 066 * <p> 067 * For example: 068 * </p> 069 * 070 * <pre>{@code 071 * DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get(); 072 * } 073 * </pre> 074 */ 075 public static class Builder extends AbstractStreamBuilder<DiskFileItemFactory, Builder> { 076 077 /** 078 * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files. 079 * <p> 080 * May be null, if tracking files is not required. 081 * </p> 082 */ 083 private FileCleaningTracker fileCleaningTracker; 084 085 /** 086 * The threshold. We do maintain this separate from the {@link #getBufferSize()}, 087 * because the parent class might change the value in {@link #setBufferSize(int)}. 088 */ 089 private int threshold; 090 091 /** 092 * Constructs a new instance. 093 */ 094 public Builder() { 095 setBufferSize(DEFAULT_THRESHOLD); 096 setPath(PathUtils.getTempDirectory()); 097 setCharset(DiskFileItem.DEFAULT_CHARSET); 098 setCharsetDefault(DiskFileItem.DEFAULT_CHARSET); 099 } 100 101 /** 102 * Constructs a new instance. 103 * <p> 104 * This builder use the aspects Path and buffer size. 105 * </p> 106 * <p> 107 * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an 108 * {@link UnsupportedOperationException}. 109 * </p> 110 * 111 * @return a new instance. 112 * @throws UnsupportedOperationException if the origin cannot provide a Path. 113 * @see AbstractOrigin#getReader(Charset) 114 */ 115 @Override 116 public DiskFileItemFactory get() { 117 return new DiskFileItemFactory(this); 118 } 119 120 /** 121 * Equivalent to {@link #getThreshold()}. 122 * @return The threshold, which is being used. 123 * @see #getThreshold() 124 * @deprecated Since 2.0.0, use {@link #getThreshold()} instead. 125 */ 126 public int getBufferSize() { 127 return getThreshold(); 128 } 129 130 /** 131 * Returns the threshold. 132 * @return The threshold. 133 */ 134 public int getThreshold() { 135 return threshold; 136 } 137 138 /** 139 * Equivalent to {@link #setThreshold(int)}. 140 * @param bufferSize The threshold, which is being used. 141 * @see #setThreshold(int) 142 * @return This builder. 143 * @deprecated Since 2.0.0, use {@link #setThreshold(int)} instead. 144 */ 145 @Override 146 public Builder setBufferSize(final int bufferSize) { 147 return setThreshold(bufferSize); 148 } 149 150 /** 151 * Sets the tracker, which is responsible for deleting temporary files. 152 * 153 * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking. 154 * @return {@code this} instance. 155 */ 156 public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) { 157 this.fileCleaningTracker = fileCleaningTracker; 158 return this; 159 } 160 161 /** 162 * Sets the threshold. The uploaded data is typically kept in memory, until 163 * a certain number of bytes (the threshold) is reached. At this point, the 164 * incoming data is transferred to a temporary file, and the in-memory data 165 * is removed. 166 * 167 * The threshold will also control the <em>state model</em> of the created 168 * instances of {@link DiskFileItem}. Details on the state model can be 169 * found {@link DiskFileItem here}. 170 * @param threshold The threshold, which is being used. 171 * @return This builder. 172 */ 173 public Builder setThreshold(final int threshold) { 174 this.threshold = threshold; 175 return this; 176 } 177 } 178 179 /** 180 * The default threshold in bytes above which uploads will be stored on disk. 181 */ 182 public static final int DEFAULT_THRESHOLD = 10_240; 183 184 /** 185 * Constructs a new {@link Builder}. 186 * 187 * @return a new {@link Builder}. 188 */ 189 public static Builder builder() { 190 return new Builder(); 191 } 192 193 /** 194 * The directory in which uploaded files will be stored, if stored on disk. 195 */ 196 private final Path repository; 197 198 /** 199 * The threshold above which uploads will be stored on disk. 200 */ 201 private final int threshold; 202 203 /** 204 * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files. 205 * <p> 206 * May be null, if tracking files is not required. 207 * </p> 208 */ 209 private final FileCleaningTracker fileCleaningTracker; 210 211 /** 212 * Default content Charset to be used when no explicit Charset parameter is provided by the sender. 213 */ 214 private final Charset charsetDefault; 215 216 /** 217 * Constructs a preconfigured instance of this class. 218 * 219 * @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold. 220 * @param threshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file. 221 * @param charsetDefault Sets the default charset for use when no explicit charset parameter is provided by the sender. 222 * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking. 223 */ 224 private DiskFileItemFactory(final Builder builder) { 225 this.threshold = builder.threshold; 226 this.repository = builder.getPath(); 227 this.charsetDefault = builder.getCharset(); 228 this.fileCleaningTracker = builder.fileCleaningTracker; 229 } 230 231 @SuppressWarnings("unchecked") 232 @Override 233 public DiskFileItem.Builder fileItemBuilder() { 234 // @formatter:off 235 return DiskFileItem.builder() 236 .setThreshold(threshold) 237 .setCharset(charsetDefault) 238 .setFileCleaningTracker(fileCleaningTracker) 239 .setPath(repository); 240 // @formatter:on 241 } 242 243 /** 244 * Gets the default charset for use when no explicit charset parameter is provided by the sender. 245 * 246 * @return the default charset 247 */ 248 public Charset getCharsetDefault() { 249 return charsetDefault; 250 } 251 252 /** 253 * Gets the tracker, which is responsible for deleting temporary files. 254 * 255 * @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked. 256 */ 257 public FileCleaningTracker getFileCleaningTracker() { 258 return fileCleaningTracker; 259 } 260 261 /** 262 * Gets the directory used to temporarily store files that are larger than the configured size threshold. 263 * 264 * @return The directory in which temporary files will be located. 265 */ 266 public Path getRepository() { 267 return repository; 268 } 269 270 /** 271 * Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes. 272 * 273 * @return The size threshold in bytes. 274 */ 275 public int getThreshold() { 276 return threshold; 277 } 278}