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 * http://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> 041 * <strong>NOTE</strong>: Files are created in the system default temporary directory with predictable names. This means that a local attacker with write access 042 * 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 043 * the uploaded file is used but could be significant. When using this implementation in an environment with local, untrusted users, 044 * {@link Builder#setPath(Path)} MUST be used to configure a repository location that is not publicly writable. In a Servlet container the location identified 045 * by the ServletContext attribute {@code javax.servlet.context.tempdir} may be used. 046 * </p> 047 * <p> 048 * 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 049 * set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must consider the following: Temporary files are automatically 050 * 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 051 * 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 052 * 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. 053 * </p> 054 * 055 * @see Builder 056 * @see Builder#get() 057 */ 058public final class DiskFileItemFactory implements FileItemFactory<DiskFileItem> { 059 060 /** 061 * Builds a new {@link DiskFileItemFactory} instance. 062 * <p> 063 * For example: 064 * </p> 065 * 066 * <pre>{@code 067 * DiskFileItemFactory factory = DiskFileItemFactory.builder().setPath(path).setBufferSize(DEFAULT_THRESHOLD).get(); 068 * } 069 * </pre> 070 */ 071 public static class Builder extends AbstractStreamBuilder<DiskFileItemFactory, Builder> { 072 073 /** 074 * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files. 075 * <p> 076 * May be null, if tracking files is not required. 077 * </p> 078 */ 079 private FileCleaningTracker fileCleaningTracker; 080 081 /** 082 * Constructs a new instance. 083 */ 084 public Builder() { 085 setBufferSize(DEFAULT_THRESHOLD); 086 setPath(PathUtils.getTempDirectory()); 087 setCharset(DiskFileItem.DEFAULT_CHARSET); 088 setCharsetDefault(DiskFileItem.DEFAULT_CHARSET); 089 } 090 091 /** 092 * Constructs a new instance. 093 * <p> 094 * This builder use the aspects Path and buffer size. 095 * </p> 096 * <p> 097 * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an 098 * {@link UnsupportedOperationException}. 099 * </p> 100 * 101 * @return a new instance. 102 * @throws UnsupportedOperationException if the origin cannot provide a Path. 103 * @see AbstractOrigin#getReader(Charset) 104 */ 105 @Override 106 public DiskFileItemFactory get() { 107 return new DiskFileItemFactory(getPath(), getBufferSize(), getCharset(), fileCleaningTracker); 108 } 109 110 /** 111 * Sets the tracker, which is responsible for deleting temporary files. 112 * 113 * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking. 114 * @return {@code this} instance. 115 */ 116 public Builder setFileCleaningTracker(final FileCleaningTracker fileCleaningTracker) { 117 this.fileCleaningTracker = fileCleaningTracker; 118 return this; 119 } 120 121 } 122 123 /** 124 * The default threshold in bytes above which uploads will be stored on disk. 125 */ 126 public static final int DEFAULT_THRESHOLD = 10_240; 127 128 /** 129 * Constructs a new {@link Builder}. 130 * 131 * @return a new {@link Builder}. 132 */ 133 public static Builder builder() { 134 return new Builder(); 135 } 136 137 /** 138 * The directory in which uploaded files will be stored, if stored on disk. 139 */ 140 private final Path repository; 141 142 /** 143 * The threshold above which uploads will be stored on disk. 144 */ 145 private final int threshold; 146 147 /** 148 * The instance of {@link FileCleaningTracker}, which is responsible for deleting temporary files. 149 * <p> 150 * May be null, if tracking files is not required. 151 * </p> 152 */ 153 private final FileCleaningTracker fileCleaningTracker; 154 155 /** 156 * Default content Charset to be used when no explicit Charset parameter is provided by the sender. 157 */ 158 private final Charset charsetDefault; 159 160 /** 161 * Constructs a preconfigured instance of this class. 162 * 163 * @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold. 164 * @param threshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file. 165 * @param charsetDefault Sets the default charset for use when no explicit charset parameter is provided by the sender. 166 * @param fileCleaningTracker Callback to track files created, or null (default) to disable tracking. 167 */ 168 private DiskFileItemFactory(final Path repository, final int threshold, final Charset charsetDefault, final FileCleaningTracker fileCleaningTracker) { 169 this.threshold = threshold; 170 this.repository = repository; 171 this.charsetDefault = charsetDefault; 172 this.fileCleaningTracker = fileCleaningTracker; 173 } 174 175 @SuppressWarnings("unchecked") 176 @Override 177 public DiskFileItem.Builder fileItemBuilder() { 178 // @formatter:off 179 return DiskFileItem.builder() 180 .setBufferSize(threshold) 181 .setCharset(charsetDefault) 182 .setFileCleaningTracker(fileCleaningTracker) 183 .setPath(repository); 184 // @formatter:on 185 } 186 187 /** 188 * Gets the default charset for use when no explicit charset parameter is provided by the sender. 189 * 190 * @return the default charset 191 */ 192 public Charset getCharsetDefault() { 193 return charsetDefault; 194 } 195 196 /** 197 * Gets the tracker, which is responsible for deleting temporary files. 198 * 199 * @return An instance of {@link FileCleaningTracker}, or null (default), if temporary files aren't tracked. 200 */ 201 public FileCleaningTracker getFileCleaningTracker() { 202 return fileCleaningTracker; 203 } 204 205 /** 206 * Gets the directory used to temporarily store files that are larger than the configured size threshold. 207 * 208 * @return The directory in which temporary files will be located. 209 */ 210 public Path getRepository() { 211 return repository; 212 } 213 214 /** 215 * Gets the size threshold beyond which files are written directly to disk. The default value is {@value #DEFAULT_THRESHOLD} bytes. 216 * 217 * @return The size threshold in bytes. 218 */ 219 public int getThreshold() { 220 return threshold; 221 } 222}