Modified: websites/production/commons/content/proper/commons-fileupload/apidocs/src-html/org/apache/commons/fileupload2/core/DiskFileItem.Builder.html ============================================================================== --- websites/production/commons/content/proper/commons-fileupload/apidocs/src-html/org/apache/commons/fileupload2/core/DiskFileItem.Builder.html (original) +++ websites/production/commons/content/proper/commons-fileupload/apidocs/src-html/org/apache/commons/fileupload2/core/DiskFileItem.Builder.html Fri Jun 6 20:13:22 2025 @@ -33,442 +33,442 @@ <span class="source-line-no">020</span><span id="line-20">import java.io.IOException;</span> <span class="source-line-no">021</span><span id="line-21">import java.io.InputStream;</span> <span class="source-line-no">022</span><span id="line-22">import java.io.OutputStream;</span> -<span class="source-line-no">023</span><span id="line-23">import java.io.UncheckedIOException;</span> -<span class="source-line-no">024</span><span id="line-24">import java.nio.charset.Charset;</span> -<span class="source-line-no">025</span><span id="line-25">import java.nio.charset.StandardCharsets;</span> -<span class="source-line-no">026</span><span id="line-26">import java.nio.file.CopyOption;</span> -<span class="source-line-no">027</span><span id="line-27">import java.nio.file.Files;</span> -<span class="source-line-no">028</span><span id="line-28">import java.nio.file.InvalidPathException;</span> -<span class="source-line-no">029</span><span id="line-29">import java.nio.file.Path;</span> -<span class="source-line-no">030</span><span id="line-30">import java.nio.file.Paths;</span> -<span class="source-line-no">031</span><span id="line-31">import java.nio.file.StandardCopyOption;</span> -<span class="source-line-no">032</span><span id="line-32">import java.util.UUID;</span> -<span class="source-line-no">033</span><span id="line-33">import java.util.concurrent.atomic.AtomicInteger;</span> -<span class="source-line-no">034</span><span id="line-34"></span> -<span class="source-line-no">035</span><span id="line-35">import org.apache.commons.fileupload2.core.FileItemFactory.AbstractFileItemBuilder;</span> -<span class="source-line-no">036</span><span id="line-36">import org.apache.commons.io.Charsets;</span> -<span class="source-line-no">037</span><span id="line-37">import org.apache.commons.io.build.AbstractOrigin;</span> -<span class="source-line-no">038</span><span id="line-38">import org.apache.commons.io.file.PathUtils;</span> -<span class="source-line-no">039</span><span id="line-39">import org.apache.commons.io.function.Uncheck;</span> -<span class="source-line-no">040</span><span id="line-40">import org.apache.commons.io.output.DeferredFileOutputStream;</span> -<span class="source-line-no">041</span><span id="line-41"></span> -<span class="source-line-no">042</span><span id="line-42">/**</span> -<span class="source-line-no">043</span><span id="line-43"> * The default implementation of the {@link FileItem FileItem} interface.</span> -<span class="source-line-no">044</span><span id="line-44"> * <p></span> -<span class="source-line-no">045</span><span id="line-45"> * After retrieving an instance of this class from a {@link DiskFileItemFactory} instance (see</span> -<span class="source-line-no">046</span><span id="line-46"> * {@code org.apache.commons.fileupload2.core.servlet.ServletFileUpload</span> -<span class="source-line-no">047</span><span id="line-47"> * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all contents of file at once using {@link #get()} or request an</span> -<span class="source-line-no">048</span><span id="line-48"> * {@link java.io.InputStream InputStream} with {@link #getInputStream()} and process the file without attempting to load it into memory, which may come handy</span> -<span class="source-line-no">049</span><span id="line-49"> * with large files.</span> -<span class="source-line-no">050</span><span id="line-50"> * </p></span> -<span class="source-line-no">051</span><span id="line-51"> * <p></span> -<span class="source-line-no">052</span><span id="line-52"> * Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a</span> -<span class="source-line-no">053</span><span id="line-53"> * {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must</span> -<span class="source-line-no">054</span><span id="line-54"> * consider the following: Temporary files are automatically deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of</span> -<span class="source-line-no">055</span><span id="line-55"> * {@link java.io.File} is garbage collected.) This is done by the so-called reaper thread, which is started and stopped automatically by the</span> -<span class="source-line-no">056</span><span id="line-56"> * {@link org.apache.commons.io.FileCleaningTracker} when there are files to be tracked. It might make sense to terminate that thread, for example, if your web</span> -<span class="source-line-no">057</span><span id="line-57"> * application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.</span> -<span class="source-line-no">058</span><span id="line-58"> * </p></span> -<span class="source-line-no">059</span><span id="line-59"> */</span> -<span class="source-line-no">060</span><span id="line-60">public final class DiskFileItem implements FileItem<DiskFileItem> {</span> -<span class="source-line-no">061</span><span id="line-61"></span> -<span class="source-line-no">062</span><span id="line-62"> /**</span> -<span class="source-line-no">063</span><span id="line-63"> * Builds a new {@link DiskFileItem} instance.</span> -<span class="source-line-no">064</span><span id="line-64"> * <p></span> -<span class="source-line-no">065</span><span id="line-65"> * For example:</span> -<span class="source-line-no">066</span><span id="line-66"> * </p></span> -<span class="source-line-no">067</span><span id="line-67"> *</span> -<span class="source-line-no">068</span><span id="line-68"> * <pre>{@code</span> -<span class="source-line-no">069</span><span id="line-69"> * final FileItem fileItem = fileItemFactory.fileItemBuilder()</span> -<span class="source-line-no">070</span><span id="line-70"> * .setFieldName("FieldName")</span> -<span class="source-line-no">071</span><span id="line-71"> * .setContentType("ContentType")</span> -<span class="source-line-no">072</span><span id="line-72"> * .setFormField(true)</span> -<span class="source-line-no">073</span><span id="line-73"> * .setFileName("FileName")</span> -<span class="source-line-no">074</span><span id="line-74"> * .setFileItemHeaders(...)</span> -<span class="source-line-no">075</span><span id="line-75"> * .get();</span> -<span class="source-line-no">076</span><span id="line-76"> * }</span> -<span class="source-line-no">077</span><span id="line-77"> * </pre></span> -<span class="source-line-no">078</span><span id="line-78"> */</span> -<span class="source-line-no">079</span><span id="line-79"> public static class Builder extends AbstractFileItemBuilder<DiskFileItem, Builder> {</span> -<span class="source-line-no">080</span><span id="line-80"></span> -<span class="source-line-no">081</span><span id="line-81"> /**</span> -<span class="source-line-no">082</span><span id="line-82"> * Constructs a new instance.</span> -<span class="source-line-no">083</span><span id="line-83"> */</span> -<span class="source-line-no">084</span><span id="line-84"> public Builder() {</span> -<span class="source-line-no">085</span><span id="line-85"> setBufferSize(DiskFileItemFactory.DEFAULT_THRESHOLD);</span> -<span class="source-line-no">086</span><span id="line-86"> setPath(PathUtils.getTempDirectory());</span> -<span class="source-line-no">087</span><span id="line-87"> setCharset(DEFAULT_CHARSET);</span> -<span class="source-line-no">088</span><span id="line-88"> setCharsetDefault(DEFAULT_CHARSET);</span> -<span class="source-line-no">089</span><span id="line-89"> }</span> -<span class="source-line-no">090</span><span id="line-90"></span> -<span class="source-line-no">091</span><span id="line-91"> /**</span> -<span class="source-line-no">092</span><span id="line-92"> * Constructs a new instance.</span> -<span class="source-line-no">093</span><span id="line-93"> * <p></span> -<span class="source-line-no">094</span><span id="line-94"> * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an</span> -<span class="source-line-no">095</span><span id="line-95"> * {@link UnsupportedOperationException}.</span> -<span class="source-line-no">096</span><span id="line-96"> * </p></span> -<span class="source-line-no">097</span><span id="line-97"> *</span> -<span class="source-line-no">098</span><span id="line-98"> * @return a new instance.</span> -<span class="source-line-no">099</span><span id="line-99"> * @throws UnsupportedOperationException if the origin cannot provide a Path.</span> -<span class="source-line-no">100</span><span id="line-100"> * @see AbstractOrigin#getReader(Charset)</span> -<span class="source-line-no">101</span><span id="line-101"> */</span> -<span class="source-line-no">102</span><span id="line-102"> @Override</span> -<span class="source-line-no">103</span><span id="line-103"> public DiskFileItem get() {</span> -<span class="source-line-no">104</span><span id="line-104"> final var diskFileItem = new DiskFileItem(getFieldName(), getContentType(), isFormField(), getFileName(), getBufferSize(), getPath(),</span> -<span class="source-line-no">105</span><span id="line-105"> getFileItemHeaders(), getCharset());</span> -<span class="source-line-no">106</span><span id="line-106"> final var tracker = getFileCleaningTracker();</span> -<span class="source-line-no">107</span><span id="line-107"> if (tracker != null) {</span> -<span class="source-line-no">108</span><span id="line-108"> tracker.track(diskFileItem.getTempFile().toFile(), diskFileItem);</span> -<span class="source-line-no">109</span><span id="line-109"> }</span> -<span class="source-line-no">110</span><span id="line-110"> return diskFileItem;</span> -<span class="source-line-no">111</span><span id="line-111"> }</span> +<span class="source-line-no">023</span><span id="line-23">import java.nio.charset.Charset;</span> +<span class="source-line-no">024</span><span id="line-24">import java.nio.charset.StandardCharsets;</span> +<span class="source-line-no">025</span><span id="line-25">import java.nio.file.CopyOption;</span> +<span class="source-line-no">026</span><span id="line-26">import java.nio.file.Files;</span> +<span class="source-line-no">027</span><span id="line-27">import java.nio.file.InvalidPathException;</span> +<span class="source-line-no">028</span><span id="line-28">import java.nio.file.Path;</span> +<span class="source-line-no">029</span><span id="line-29">import java.nio.file.Paths;</span> +<span class="source-line-no">030</span><span id="line-30">import java.nio.file.StandardCopyOption;</span> +<span class="source-line-no">031</span><span id="line-31">import java.util.UUID;</span> +<span class="source-line-no">032</span><span id="line-32">import java.util.concurrent.atomic.AtomicInteger;</span> +<span class="source-line-no">033</span><span id="line-33"></span> +<span class="source-line-no">034</span><span id="line-34">import org.apache.commons.fileupload2.core.FileItemFactory.AbstractFileItemBuilder;</span> +<span class="source-line-no">035</span><span id="line-35">import org.apache.commons.io.Charsets;</span> +<span class="source-line-no">036</span><span id="line-36">import org.apache.commons.io.build.AbstractOrigin;</span> +<span class="source-line-no">037</span><span id="line-37">import org.apache.commons.io.file.PathUtils;</span> +<span class="source-line-no">038</span><span id="line-38">import org.apache.commons.io.output.DeferredFileOutputStream;</span> +<span class="source-line-no">039</span><span id="line-39"></span> +<span class="source-line-no">040</span><span id="line-40">/**</span> +<span class="source-line-no">041</span><span id="line-41"> * The default implementation of the {@link FileItem FileItem} interface.</span> +<span class="source-line-no">042</span><span id="line-42"> * <p></span> +<span class="source-line-no">043</span><span id="line-43"> * After retrieving an instance of this class from a {@link DiskFileItemFactory} instance (see</span> +<span class="source-line-no">044</span><span id="line-44"> * {@code org.apache.commons.fileupload2.core.servlet.ServletFileUpload</span> +<span class="source-line-no">045</span><span id="line-45"> * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may either request all contents of file at once using {@link #get()} or request an</span> +<span class="source-line-no">046</span><span id="line-46"> * {@link java.io.InputStream InputStream} with {@link #getInputStream()} and process the file without attempting to load it into memory, which may come handy</span> +<span class="source-line-no">047</span><span id="line-47"> * with large files.</span> +<span class="source-line-no">048</span><span id="line-48"> * </p></span> +<span class="source-line-no">049</span><span id="line-49"> * <p></span> +<span class="source-line-no">050</span><span id="line-50"> * Temporary files, which are created for file items, should be deleted later on. The best way to do this is using a</span> +<span class="source-line-no">051</span><span id="line-51"> * {@link org.apache.commons.io.FileCleaningTracker}, which you can set on the {@link DiskFileItemFactory}. However, if you do use such a tracker, then you must</span> +<span class="source-line-no">052</span><span id="line-52"> * consider the following: Temporary files are automatically deleted as soon as they are no longer needed. (More precisely, when the corresponding instance of</span> +<span class="source-line-no">053</span><span id="line-53"> * {@link java.io.File} is garbage collected.) This is done by the so-called reaper thread, which is started and stopped automatically by the</span> +<span class="source-line-no">054</span><span id="line-54"> * {@link org.apache.commons.io.FileCleaningTracker} when there are files to be tracked. It might make sense to terminate that thread, for example, if your web</span> +<span class="source-line-no">055</span><span id="line-55"> * application ends. See the section on "Resource cleanup" in the users guide of Commons FileUpload.</span> +<span class="source-line-no">056</span><span id="line-56"> * </p></span> +<span class="source-line-no">057</span><span id="line-57"> */</span> +<span class="source-line-no">058</span><span id="line-58">public final class DiskFileItem implements FileItem<DiskFileItem> {</span> +<span class="source-line-no">059</span><span id="line-59"></span> +<span class="source-line-no">060</span><span id="line-60"> /**</span> +<span class="source-line-no">061</span><span id="line-61"> * Builds a new {@link DiskFileItem} instance.</span> +<span class="source-line-no">062</span><span id="line-62"> * <p></span> +<span class="source-line-no">063</span><span id="line-63"> * For example:</span> +<span class="source-line-no">064</span><span id="line-64"> * </p></span> +<span class="source-line-no">065</span><span id="line-65"> *</span> +<span class="source-line-no">066</span><span id="line-66"> * <pre>{@code</span> +<span class="source-line-no">067</span><span id="line-67"> * final FileItem fileItem = fileItemFactory.fileItemBuilder()</span> +<span class="source-line-no">068</span><span id="line-68"> * .setFieldName("FieldName")</span> +<span class="source-line-no">069</span><span id="line-69"> * .setContentType("ContentType")</span> +<span class="source-line-no">070</span><span id="line-70"> * .setFormField(true)</span> +<span class="source-line-no">071</span><span id="line-71"> * .setFileName("FileName")</span> +<span class="source-line-no">072</span><span id="line-72"> * .setFileItemHeaders(...)</span> +<span class="source-line-no">073</span><span id="line-73"> * .get();</span> +<span class="source-line-no">074</span><span id="line-74"> * }</span> +<span class="source-line-no">075</span><span id="line-75"> * </pre></span> +<span class="source-line-no">076</span><span id="line-76"> */</span> +<span class="source-line-no">077</span><span id="line-77"> public static class Builder extends AbstractFileItemBuilder<DiskFileItem, Builder> {</span> +<span class="source-line-no">078</span><span id="line-78"></span> +<span class="source-line-no">079</span><span id="line-79"> /**</span> +<span class="source-line-no">080</span><span id="line-80"> * Constructs a new instance.</span> +<span class="source-line-no">081</span><span id="line-81"> */</span> +<span class="source-line-no">082</span><span id="line-82"> public Builder() {</span> +<span class="source-line-no">083</span><span id="line-83"> setBufferSize(DiskFileItemFactory.DEFAULT_THRESHOLD);</span> +<span class="source-line-no">084</span><span id="line-84"> setPath(PathUtils.getTempDirectory());</span> +<span class="source-line-no">085</span><span id="line-85"> setCharset(DEFAULT_CHARSET);</span> +<span class="source-line-no">086</span><span id="line-86"> setCharsetDefault(DEFAULT_CHARSET);</span> +<span class="source-line-no">087</span><span id="line-87"> }</span> +<span class="source-line-no">088</span><span id="line-88"></span> +<span class="source-line-no">089</span><span id="line-89"> /**</span> +<span class="source-line-no">090</span><span id="line-90"> * Constructs a new instance.</span> +<span class="source-line-no">091</span><span id="line-91"> * <p></span> +<span class="source-line-no">092</span><span id="line-92"> * You must provide an origin that can be converted to a Reader by this builder, otherwise, this call will throw an</span> +<span class="source-line-no">093</span><span id="line-93"> * {@link UnsupportedOperationException}.</span> +<span class="source-line-no">094</span><span id="line-94"> * </p></span> +<span class="source-line-no">095</span><span id="line-95"> *</span> +<span class="source-line-no">096</span><span id="line-96"> * @return a new instance.</span> +<span class="source-line-no">097</span><span id="line-97"> * @throws UnsupportedOperationException if the origin cannot provide a Path.</span> +<span class="source-line-no">098</span><span id="line-98"> * @see AbstractOrigin#getReader(Charset)</span> +<span class="source-line-no">099</span><span id="line-99"> */</span> +<span class="source-line-no">100</span><span id="line-100"> @Override</span> +<span class="source-line-no">101</span><span id="line-101"> public DiskFileItem get() {</span> +<span class="source-line-no">102</span><span id="line-102"> final var diskFileItem = new DiskFileItem(getFieldName(), getContentType(), isFormField(), getFileName(), getBufferSize(), getPath(),</span> +<span class="source-line-no">103</span><span id="line-103"> getFileItemHeaders(), getCharset());</span> +<span class="source-line-no">104</span><span id="line-104"> final var tracker = getFileCleaningTracker();</span> +<span class="source-line-no">105</span><span id="line-105"> if (tracker != null) {</span> +<span class="source-line-no">106</span><span id="line-106"> tracker.track(diskFileItem.getTempFile().toFile(), diskFileItem);</span> +<span class="source-line-no">107</span><span id="line-107"> }</span> +<span class="source-line-no">108</span><span id="line-108"> return diskFileItem;</span> +<span class="source-line-no">109</span><span id="line-109"> }</span> +<span class="source-line-no">110</span><span id="line-110"></span> +<span class="source-line-no">111</span><span id="line-111"> }</span> <span class="source-line-no">112</span><span id="line-112"></span> -<span class="source-line-no">113</span><span id="line-113"> }</span> -<span class="source-line-no">114</span><span id="line-114"></span> -<span class="source-line-no">115</span><span id="line-115"> /**</span> -<span class="source-line-no">116</span><span id="line-116"> * Default content charset to be used when no explicit charset parameter is provided by the sender. Media subtypes of the "text" type are defined to have a</span> -<span class="source-line-no">117</span><span id="line-117"> * default charset value of "ISO-8859-1" when received via HTTP.</span> -<span class="source-line-no">118</span><span id="line-118"> */</span> -<span class="source-line-no">119</span><span id="line-119"> public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;</span> -<span class="source-line-no">120</span><span id="line-120"></span> -<span class="source-line-no">121</span><span id="line-121"> /**</span> -<span class="source-line-no">122</span><span id="line-122"> * UID used in unique file name generation.</span> -<span class="source-line-no">123</span><span id="line-123"> */</span> -<span class="source-line-no">124</span><span id="line-124"> private static final String UID = UUID.randomUUID().toString().replace('-', '_');</span> -<span class="source-line-no">125</span><span id="line-125"></span> -<span class="source-line-no">126</span><span id="line-126"> /**</span> -<span class="source-line-no">127</span><span id="line-127"> * Counter used in unique identifier generation.</span> -<span class="source-line-no">128</span><span id="line-128"> */</span> -<span class="source-line-no">129</span><span id="line-129"> private static final AtomicInteger COUNTER = new AtomicInteger();</span> -<span class="source-line-no">130</span><span id="line-130"></span> -<span class="source-line-no">131</span><span id="line-131"> /**</span> -<span class="source-line-no">132</span><span id="line-132"> * Constructs a new {@link Builder}.</span> -<span class="source-line-no">133</span><span id="line-133"> *</span> -<span class="source-line-no">134</span><span id="line-134"> * @return a new {@link Builder}.</span> -<span class="source-line-no">135</span><span id="line-135"> */</span> -<span class="source-line-no">136</span><span id="line-136"> public static Builder builder() {</span> -<span class="source-line-no">137</span><span id="line-137"> return new Builder();</span> -<span class="source-line-no">138</span><span id="line-138"> }</span> -<span class="source-line-no">139</span><span id="line-139"></span> -<span class="source-line-no">140</span><span id="line-140"> /**</span> -<span class="source-line-no">141</span><span id="line-141"> * Tests if the file name is valid. For example, if it contains a NUL characters, it's invalid. If the file name is valid, it will be returned without any</span> -<span class="source-line-no">142</span><span id="line-142"> * modifications. Otherwise, throw an {@link InvalidPathException}.</span> -<span class="source-line-no">143</span><span id="line-143"> *</span> -<span class="source-line-no">144</span><span id="line-144"> * @param fileName The file name to check</span> -<span class="source-line-no">145</span><span id="line-145"> * @return Unmodified file name, if valid.</span> -<span class="source-line-no">146</span><span id="line-146"> * @throws InvalidPathException The file name is invalid.</span> -<span class="source-line-no">147</span><span id="line-147"> */</span> -<span class="source-line-no">148</span><span id="line-148"> public static String checkFileName(final String fileName) {</span> -<span class="source-line-no">149</span><span id="line-149"> if (fileName != null) {</span> -<span class="source-line-no">150</span><span id="line-150"> // Specific NUL check to build a better exception message.</span> -<span class="source-line-no">151</span><span id="line-151"> final var indexOf0 = fileName.indexOf(0);</span> -<span class="source-line-no">152</span><span id="line-152"> if (indexOf0 != -1) {</span> -<span class="source-line-no">153</span><span id="line-153"> final var sb = new StringBuilder();</span> -<span class="source-line-no">154</span><span id="line-154"> for (var i = 0; i < fileName.length(); i++) {</span> -<span class="source-line-no">155</span><span id="line-155"> final var c = fileName.charAt(i);</span> -<span class="source-line-no">156</span><span id="line-156"> switch (c) {</span> -<span class="source-line-no">157</span><span id="line-157"> case 0:</span> -<span class="source-line-no">158</span><span id="line-158"> sb.append("\\0");</span> -<span class="source-line-no">159</span><span id="line-159"> break;</span> -<span class="source-line-no">160</span><span id="line-160"> default:</span> -<span class="source-line-no">161</span><span id="line-161"> sb.append(c);</span> -<span class="source-line-no">162</span><span id="line-162"> break;</span> -<span class="source-line-no">163</span><span id="line-163"> }</span> -<span class="source-line-no">164</span><span id="line-164"> }</span> -<span class="source-line-no">165</span><span id="line-165"> throw new InvalidPathException(fileName, sb.toString(), indexOf0);</span> -<span class="source-line-no">166</span><span id="line-166"> }</span> -<span class="source-line-no">167</span><span id="line-167"> // Throws InvalidPathException on invalid file names</span> -<span class="source-line-no">168</span><span id="line-168"> Paths.get(fileName);</span> -<span class="source-line-no">169</span><span id="line-169"> }</span> -<span class="source-line-no">170</span><span id="line-170"> return fileName;</span> -<span class="source-line-no">171</span><span id="line-171"> }</span> -<span class="source-line-no">172</span><span id="line-172"></span> -<span class="source-line-no">173</span><span id="line-173"> /**</span> -<span class="source-line-no">174</span><span id="line-174"> * Gets an identifier that is unique within the class loader used to load this class, but does not have random-like appearance.</span> -<span class="source-line-no">175</span><span id="line-175"> *</span> -<span class="source-line-no">176</span><span id="line-176"> * @return A String with the non-random looking instance identifier.</span> -<span class="source-line-no">177</span><span id="line-177"> */</span> -<span class="source-line-no">178</span><span id="line-178"> private static String getUniqueId() {</span> -<span class="source-line-no">179</span><span id="line-179"> final var limit = 100_000_000;</span> -<span class="source-line-no">180</span><span id="line-180"> final var current = COUNTER.getAndIncrement();</span> -<span class="source-line-no">181</span><span id="line-181"> var id = Integer.toString(current);</span> -<span class="source-line-no">182</span><span id="line-182"></span> -<span class="source-line-no">183</span><span id="line-183"> // If you manage to get more than 100 million of ids, you'll</span> -<span class="source-line-no">184</span><span id="line-184"> // start getting ids longer than 8 characters.</span> -<span class="source-line-no">185</span><span id="line-185"> if (current < limit) {</span> -<span class="source-line-no">186</span><span id="line-186"> id = ("00000000" + id).substring(id.length());</span> -<span class="source-line-no">187</span><span id="line-187"> }</span> -<span class="source-line-no">188</span><span id="line-188"> return id;</span> -<span class="source-line-no">189</span><span id="line-189"> }</span> -<span class="source-line-no">190</span><span id="line-190"></span> -<span class="source-line-no">191</span><span id="line-191"> /**</span> -<span class="source-line-no">192</span><span id="line-192"> * The name of the form field as provided by the browser.</span> -<span class="source-line-no">193</span><span id="line-193"> */</span> -<span class="source-line-no">194</span><span id="line-194"> private String fieldName;</span> -<span class="source-line-no">195</span><span id="line-195"></span> -<span class="source-line-no">196</span><span id="line-196"> /**</span> -<span class="source-line-no">197</span><span id="line-197"> * The content type passed by the browser, or {@code null} if not defined.</span> -<span class="source-line-no">198</span><span id="line-198"> */</span> -<span class="source-line-no">199</span><span id="line-199"> private final String contentType;</span> -<span class="source-line-no">200</span><span id="line-200"></span> -<span class="source-line-no">201</span><span id="line-201"> /**</span> -<span class="source-line-no">202</span><span id="line-202"> * Whether or not this item is a simple form field.</span> -<span class="source-line-no">203</span><span id="line-203"> */</span> -<span class="source-line-no">204</span><span id="line-204"> private volatile boolean isFormField;</span> -<span class="source-line-no">205</span><span id="line-205"></span> -<span class="source-line-no">206</span><span id="line-206"> /**</span> -<span class="source-line-no">207</span><span id="line-207"> * The original file name in the user's file system.</span> -<span class="source-line-no">208</span><span id="line-208"> */</span> -<span class="source-line-no">209</span><span id="line-209"> private final String fileName;</span> -<span class="source-line-no">210</span><span id="line-210"></span> -<span class="source-line-no">211</span><span id="line-211"> /**</span> -<span class="source-line-no">212</span><span id="line-212"> * The size of the item, in bytes. This is used to cache the size when a file item is moved from its original location.</span> -<span class="source-line-no">213</span><span id="line-213"> */</span> -<span class="source-line-no">214</span><span id="line-214"> private volatile long size = -1;</span> -<span class="source-line-no">215</span><span id="line-215"></span> -<span class="source-line-no">216</span><span id="line-216"> /**</span> -<span class="source-line-no">217</span><span id="line-217"> * The threshold above which uploads will be stored on disk.</span> -<span class="source-line-no">218</span><span id="line-218"> */</span> -<span class="source-line-no">219</span><span id="line-219"> private final int threshold;</span> -<span class="source-line-no">220</span><span id="line-220"></span> -<span class="source-line-no">221</span><span id="line-221"> /**</span> -<span class="source-line-no">222</span><span id="line-222"> * The directory in which uploaded files will be stored, if stored on disk.</span> -<span class="source-line-no">223</span><span id="line-223"> */</span> -<span class="source-line-no">224</span><span id="line-224"> private final Path repository;</span> -<span class="source-line-no">225</span><span id="line-225"></span> -<span class="source-line-no">226</span><span id="line-226"> /**</span> -<span class="source-line-no">227</span><span id="line-227"> * Cached contents of the file.</span> -<span class="source-line-no">228</span><span id="line-228"> */</span> -<span class="source-line-no">229</span><span id="line-229"> private byte[] cachedContent;</span> -<span class="source-line-no">230</span><span id="line-230"></span> -<span class="source-line-no">231</span><span id="line-231"> /**</span> -<span class="source-line-no">232</span><span id="line-232"> * Output stream for this item.</span> -<span class="source-line-no">233</span><span id="line-233"> */</span> -<span class="source-line-no">234</span><span id="line-234"> private DeferredFileOutputStream dfos;</span> -<span class="source-line-no">235</span><span id="line-235"></span> -<span class="source-line-no">236</span><span id="line-236"> /**</span> -<span class="source-line-no">237</span><span id="line-237"> * The temporary file to use.</span> -<span class="source-line-no">238</span><span id="line-238"> */</span> -<span class="source-line-no">239</span><span id="line-239"> private final Path tempFile;</span> -<span class="source-line-no">240</span><span id="line-240"></span> -<span class="source-line-no">241</span><span id="line-241"> /**</span> -<span class="source-line-no">242</span><span id="line-242"> * The file items headers.</span> -<span class="source-line-no">243</span><span id="line-243"> */</span> -<span class="source-line-no">244</span><span id="line-244"> private FileItemHeaders fileItemHeaders;</span> -<span class="source-line-no">245</span><span id="line-245"></span> -<span class="source-line-no">246</span><span id="line-246"> /**</span> -<span class="source-line-no">247</span><span id="line-247"> * Default content Charset to be used when no explicit Charset parameter is provided by the sender.</span> -<span class="source-line-no">248</span><span id="line-248"> */</span> -<span class="source-line-no">249</span><span id="line-249"> private Charset charsetDefault = DEFAULT_CHARSET;</span> -<span class="source-line-no">250</span><span id="line-250"></span> -<span class="source-line-no">251</span><span id="line-251"> /**</span> -<span class="source-line-no">252</span><span id="line-252"> * Constructs a new {@code DiskFileItem} instance.</span> -<span class="source-line-no">253</span><span id="line-253"> *</span> -<span class="source-line-no">254</span><span id="line-254"> * @param fieldName The name of the form field.</span> -<span class="source-line-no">255</span><span id="line-255"> * @param contentType The content type passed by the browser or {@code null} if not specified.</span> -<span class="source-line-no">256</span><span id="line-256"> * @param isFormField Whether or not this item is a plain form field, as opposed to a file upload.</span> -<span class="source-line-no">257</span><span id="line-257"> * @param fileName The original file name in the user's file system, or {@code null} if not specified.</span> -<span class="source-line-no">258</span><span id="line-258"> * @param threshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.</span> -<span class="source-line-no">259</span><span id="line-259"> * @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold.</span> -<span class="source-line-no">260</span><span id="line-260"> * @param fileItemHeaders The file item headers.</span> -<span class="source-line-no">261</span><span id="line-261"> * @param defaultCharset The default Charset.</span> -<span class="source-line-no">262</span><span id="line-262"> */</span> -<span class="source-line-no">263</span><span id="line-263"> private DiskFileItem(final String fieldName, final String contentType, final boolean isFormField, final String fileName, final int threshold,</span> -<span class="source-line-no">264</span><span id="line-264"> final Path repository, final FileItemHeaders fileItemHeaders, final Charset defaultCharset) {</span> -<span class="source-line-no">265</span><span id="line-265"> this.fieldName = fieldName;</span> -<span class="source-line-no">266</span><span id="line-266"> this.contentType = contentType;</span> -<span class="source-line-no">267</span><span id="line-267"> this.charsetDefault = defaultCharset;</span> -<span class="source-line-no">268</span><span id="line-268"> this.isFormField = isFormField;</span> -<span class="source-line-no">269</span><span id="line-269"> this.fileName = fileName;</span> -<span class="source-line-no">270</span><span id="line-270"> this.fileItemHeaders = fileItemHeaders;</span> -<span class="source-line-no">271</span><span id="line-271"> this.threshold = threshold;</span> -<span class="source-line-no">272</span><span id="line-272"> this.repository = repository != null ? repository : PathUtils.getTempDirectory();</span> -<span class="source-line-no">273</span><span id="line-273"> this.tempFile = this.repository.resolve(String.format("upload_%s_%s.tmp", UID, getUniqueId()));</span> -<span class="source-line-no">274</span><span id="line-274"> }</span> -<span class="source-line-no">275</span><span id="line-275"></span> -<span class="source-line-no">276</span><span id="line-276"> /**</span> -<span class="source-line-no">277</span><span id="line-277"> * Deletes the underlying storage for a file item, including deleting any associated temporary disk file. This method can be used to ensure that this is</span> -<span class="source-line-no">278</span><span id="line-278"> * done at an earlier time, thus preserving system resources.</span> -<span class="source-line-no">279</span><span id="line-279"> *</span> -<span class="source-line-no">280</span><span id="line-280"> * @throws IOException if an error occurs.</span> -<span class="source-line-no">281</span><span id="line-281"> */</span> -<span class="source-line-no">282</span><span id="line-282"> @Override</span> -<span class="source-line-no">283</span><span id="line-283"> public DiskFileItem delete() throws IOException {</span> -<span class="source-line-no">284</span><span id="line-284"> cachedContent = null;</span> -<span class="source-line-no">285</span><span id="line-285"> final var outputFile = getPath();</span> -<span class="source-line-no">286</span><span id="line-286"> if (outputFile != null && !isInMemory() && Files.exists(outputFile)) {</span> -<span class="source-line-no">287</span><span id="line-287"> Files.delete(outputFile);</span> -<span class="source-line-no">288</span><span id="line-288"> }</span> -<span class="source-line-no">289</span><span id="line-289"> return this;</span> -<span class="source-line-no">290</span><span id="line-290"> }</span> -<span class="source-line-no">291</span><span id="line-291"></span> -<span class="source-line-no">292</span><span id="line-292"> /**</span> -<span class="source-line-no">293</span><span id="line-293"> * Gets the contents of the file as an array of bytes. If the contents of the file were not yet cached in memory, they will be loaded from the disk storage</span> -<span class="source-line-no">294</span><span id="line-294"> * and cached.</span> -<span class="source-line-no">295</span><span id="line-295"> *</span> -<span class="source-line-no">296</span><span id="line-296"> * @return The contents of the file as an array of bytes or {@code null} if the data cannot be read.</span> -<span class="source-line-no">297</span><span id="line-297"> * @throws UncheckedIOException if an I/O error occurs.</span> -<span class="source-line-no">298</span><span id="line-298"> * @throws OutOfMemoryError See {@link Files#readAllBytes(Path)}: If an array of the required size cannot be allocated, for example the file is larger</span> -<span class="source-line-no">299</span><span id="line-299"> * that {@code 2GB}</span> -<span class="source-line-no">300</span><span id="line-300"> */</span> -<span class="source-line-no">301</span><span id="line-301"> @Override</span> -<span class="source-line-no">302</span><span id="line-302"> public byte[] get() throws UncheckedIOException {</span> -<span class="source-line-no">303</span><span id="line-303"> if (isInMemory()) {</span> -<span class="source-line-no">304</span><span id="line-304"> if (cachedContent == null && dfos != null) {</span> -<span class="source-line-no">305</span><span id="line-305"> cachedContent = dfos.getData();</span> -<span class="source-line-no">306</span><span id="line-306"> }</span> -<span class="source-line-no">307</span><span id="line-307"> return cachedContent != null ? cachedContent.clone() : new byte[0];</span> -<span class="source-line-no">308</span><span id="line-308"> }</span> -<span class="source-line-no">309</span><span id="line-309"> return Uncheck.get(() -> Files.readAllBytes(dfos.getFile().toPath()));</span> -<span class="source-line-no">310</span><span id="line-310"> }</span> -<span class="source-line-no">311</span><span id="line-311"></span> -<span class="source-line-no">312</span><span id="line-312"> /**</span> -<span class="source-line-no">313</span><span id="line-313"> * Gets the content charset passed by the agent or {@code null} if not defined.</span> -<span class="source-line-no">314</span><span id="line-314"> *</span> -<span class="source-line-no">315</span><span id="line-315"> * @return The content charset passed by the agent or {@code null} if not defined.</span> -<span class="source-line-no">316</span><span id="line-316"> */</span> -<span class="source-line-no">317</span><span id="line-317"> public Charset getCharset() {</span> -<span class="source-line-no">318</span><span id="line-318"> final var parser = new ParameterParser();</span> -<span class="source-line-no">319</span><span id="line-319"> parser.setLowerCaseNames(true);</span> -<span class="source-line-no">320</span><span id="line-320"> // Parameter parser can handle null input</span> -<span class="source-line-no">321</span><span id="line-321"> final var params = parser.parse(getContentType(), ';');</span> -<span class="source-line-no">322</span><span id="line-322"> return Charsets.toCharset(params.get("charset"), charsetDefault);</span> -<span class="source-line-no">323</span><span id="line-323"> }</span> -<span class="source-line-no">324</span><span id="line-324"></span> -<span class="source-line-no">325</span><span id="line-325"> /**</span> -<span class="source-line-no">326</span><span id="line-326"> * Gets the default charset for use when no explicit charset parameter is provided by the sender.</span> -<span class="source-line-no">327</span><span id="line-327"> *</span> -<span class="source-line-no">328</span><span id="line-328"> * @return the default charset</span> -<span class="source-line-no">329</span><span id="line-329"> */</span> -<span class="source-line-no">330</span><span id="line-330"> public Charset getCharsetDefault() {</span> -<span class="source-line-no">331</span><span id="line-331"> return charsetDefault;</span> -<span class="source-line-no">332</span><span id="line-332"> }</span> -<span class="source-line-no">333</span><span id="line-333"></span> -<span class="source-line-no">334</span><span id="line-334"> /**</span> -<span class="source-line-no">335</span><span id="line-335"> * Gets the content type passed by the agent or {@code null} if not defined.</span> -<span class="source-line-no">336</span><span id="line-336"> *</span> -<span class="source-line-no">337</span><span id="line-337"> * @return The content type passed by the agent or {@code null} if not defined.</span> -<span class="source-line-no">338</span><span id="line-338"> */</span> -<span class="source-line-no">339</span><span id="line-339"> @Override</span> -<span class="source-line-no">340</span><span id="line-340"> public String getContentType() {</span> -<span class="source-line-no">341</span><span id="line-341"> return contentType;</span> -<span class="source-line-no">342</span><span id="line-342"> }</span> -<span class="source-line-no">343</span><span id="line-343"></span> -<span class="source-line-no">344</span><span id="line-344"> /**</span> -<span class="source-line-no">345</span><span id="line-345"> * Gets the name of the field in the multipart form corresponding to this file item.</span> -<span class="source-line-no">346</span><span id="line-346"> *</span> -<span class="source-line-no">347</span><span id="line-347"> * @return The name of the form field.</span> -<span class="source-line-no">348</span><span id="line-348"> * @see #setFieldName(String)</span> -<span class="source-line-no">349</span><span id="line-349"> */</span> -<span class="source-line-no">350</span><span id="line-350"> @Override</span> -<span class="source-line-no">351</span><span id="line-351"> public String getFieldName() {</span> -<span class="source-line-no">352</span><span id="line-352"> return fieldName;</span> -<span class="source-line-no">353</span><span id="line-353"> }</span> -<span class="source-line-no">354</span><span id="line-354"></span> -<span class="source-line-no">355</span><span id="line-355"> /**</span> -<span class="source-line-no">356</span><span id="line-356"> * Gets the file item headers.</span> -<span class="source-line-no">357</span><span id="line-357"> *</span> -<span class="source-line-no">358</span><span id="line-358"> * @return The file items headers.</span> -<span class="source-line-no">359</span><span id="line-359"> */</span> -<span class="source-line-no">360</span><span id="line-360"> @Override</span> -<span class="source-line-no">361</span><span id="line-361"> public FileItemHeaders getHeaders() {</span> -<span class="source-line-no">362</span><span id="line-362"> return fileItemHeaders;</span> -<span class="source-line-no">363</span><span id="line-363"> }</span> -<span class="source-line-no">364</span><span id="line-364"></span> -<span class="source-line-no">365</span><span id="line-365"> /**</span> -<span class="source-line-no">366</span><span id="line-366"> * Gets an {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file.</span> -<span class="source-line-no">367</span><span id="line-367"> *</span> -<span class="source-line-no">368</span><span id="line-368"> * @return An {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file.</span> -<span class="source-line-no">369</span><span id="line-369"> * @throws IOException if an error occurs.</span> -<span class="source-line-no">370</span><span id="line-370"> */</span> -<span class="source-line-no">371</span><span id="line-371"> @Override</span> -<span class="source-line-no">372</span><span id="line-372"> public InputStream getInputStream() throws IOException {</span> -<span class="source-line-no">373</span><span id="line-373"> if (!isInMemory()) {</span> -<span class="source-line-no">374</span><span id="line-374"> return Files.newInputStream(dfos.getFile().toPath());</span> -<span class="source-line-no">375</span><span id="line-375"> }</span> -<span class="source-line-no">376</span><span id="line-376"></span> -<span class="source-line-no">377</span><span id="line-377"> if (cachedContent == null) {</span> -<span class="source-line-no">378</span><span id="line-378"> cachedContent = dfos.getData();</span> -<span class="source-line-no">379</span><span id="line-379"> }</span> -<span class="source-line-no">380</span><span id="line-380"> return new ByteArrayInputStream(cachedContent);</span> -<span class="source-line-no">381</span><span id="line-381"> }</span> -<span class="source-line-no">382</span><span id="line-382"></span> -<span class="source-line-no">383</span><span id="line-383"> /**</span> -<span class="source-line-no">384</span><span id="line-384"> * Gets the original file name in the client's file system.</span> -<span class="source-line-no">385</span><span id="line-385"> *</span> -<span class="source-line-no">386</span><span id="line-386"> * @return The original file name in the client's file system.</span> -<span class="source-line-no">387</span><span id="line-387"> * @throws InvalidPathException The file name contains a NUL character, which might be an indicator of a security attack. If you intend to use the file name</span> -<span class="source-line-no">388</span><span id="line-388"> * anyways, catch the exception and use {@link InvalidPathException#getInput()}.</span> -<span class="source-line-no">389</span><span id="line-389"> */</span> -<span class="source-line-no">390</span><span id="line-390"> @Override</span> -<span class="source-line-no">391</span><span id="line-391"> public String getName() {</span> -<span class="source-line-no">392</span><span id="line-392"> return checkFileName(fileName);</span> -<span class="source-line-no">393</span><span id="line-393"> }</span> -<span class="source-line-no">394</span><span id="line-394"></span> -<span class="source-line-no">395</span><span id="line-395"> /**</span> -<span class="source-line-no">396</span><span id="line-396"> * Gets an {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file.</span> -<span class="source-line-no">397</span><span id="line-397"> *</span> -<span class="source-line-no">398</span><span id="line-398"> * @return An {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file.</span> -<span class="source-line-no">399</span><span id="line-399"> */</span> -<span class="source-line-no">400</span><span id="line-400"> @Override</span> -<span class="source-line-no">401</span><span id="line-401"> public OutputStream getOutputStream() {</span> -<span class="source-line-no">402</span><span id="line-402"> if (dfos == null) {</span> -<span class="source-line-no">403</span><span id="line-403"> dfos = DeferredFileOutputStream.builder().setThreshold(threshold).setOutputFile(getTempFile().toFile()).get();</span> -<span class="source-line-no">404</span><span id="line-404"> }</span> -<span class="source-line-no">405</span><span id="line-405"> return dfos;</span> -<span class="source-line-no">406</span><span id="line-406"> }</span> -<span class="source-line-no">407</span><span id="line-407"></span> -<span class="source-line-no">408</span><span id="line-408"> /**</span> -<span class="source-line-no">409</span><span id="line-409"> * Gets the {@link Path} for the {@code FileItem}'s data's temporary location on the disk. Note that for {@code FileItem}s that have their data stored in</span> -<span class="source-line-no">410</span><span id="line-410"> * memory, this method will return {@code null}. When handling large files, you can use {@link Files#move(Path,Path,CopyOption...)} to move the file to new</span> -<span class="source-line-no">411</span><span id="line-411"> * location without copying the data, if the source and destination locations reside within the same logical volume.</span> -<span class="source-line-no">412</span><span id="line-412"> *</span> -<span class="source-line-no">413</span><span id="line-413"> * @return The data file, or {@code null} if the data is stored in memory.</span> -<span class="source-line-no">414</span><span id="line-414"> */</span> -<span class="source-line-no">415</span><span id="line-415"> public Path getPath() {</span> -<span class="source-line-no">416</span><span id="line-416"> if (dfos == null) {</span> -<span class="source-line-no">417</span><span id="line-417"> return null;</span> -<span class="source-line-no">418</span><span id="line-418"> }</span> -<span class="source-line-no">419</span><span id="line-419"> if (isInMemory()) {</span> -<span class="source-line-no">420</span><span id="line-420"> return null;</span> -<span class="source-line-no">421</span><span id="line-421"> }</span> -<span class="source-line-no">422</span><span id="line-422"> return dfos.getFile().toPath();</span> -<span class="source-line-no">423</span><span id="line-423"> }</span> -<span class="source-line-no">424</span><span id="line-424"></span> -<span class="source-line-no">425</span><span id="line-425"> /**</span> -<span class="source-line-no">426</span><span id="line-426"> * Gets the size of the file.</span> -<span class="source-line-no">427</span><span id="line-427"> *</span> -<span class="source-line-no">428</span><span id="line-428"> * @return The size of the file, in bytes.</span> -<span class="source-line-no">429</span><span id="line-429"> */</span> -<span class="source-line-no">430</span><span id="line-430"> @Override</span> -<span class="source-line-no">431</span><span id="line-431"> public long getSize() {</span> -<span class="source-line-no">432</span><span id="line-432"> if (size >= 0) {</span> -<span class="source-line-no">433</span><span id="line-433"> return size;</span> -<span class="source-line-no">434</span><span id="line-434"> }</span> -<span class="source-line-no">435</span><span id="line-435"> if (cachedContent != null) {</span> -<span class="source-line-no">436</span><span id="line-436"> return cachedContent.length;</span> -<span class="source-line-no">437</span><span id="line-437"> }</span> -<span class="source-line-no">438</span><span id="line-438"> return dfos != null ? dfos.getByteCount() : 0;</span> -<span class="source-line-no">439</span><span id="line-439"> }</span> -<span class="source-line-no">440</span><span id="line-440"></span> -<span class="source-line-no">441</span><span id="line-441"> /**</span> -<span class="source-line-no">442</span><span id="line-442"> * Gets the contents of the file as a String, using the default character encoding. This method uses {@link #get()} to retrieve the contents of the file.</span> -<span class="source-line-no">443</span><span id="line-443"> * <p></span> -<span class="source-line-no">444</span><span id="line-444"> * <strong>TODO</strong> Consider making this method throw UnsupportedEncodingException.</span> -<span class="source-line-no">445</span><span id="line-445"> * </p></span> -<span class="source-line-no">446</span><span id="line-446"> *</span> -<span class="source-line-no">447</span><span id="line-447"> * @return The contents of the file, as a string.</span> -<span class="source-line-no">448</span><span id="line-448"> */</span> -<span class="source-line-no">449</span><span id="line-449"> @Override</span> -<span class="source-line-no">450</span><span id="line-450"> public String getString() {</span> -<span class="source-line-no">451</span><span id="line-451"> return new String(get(), getCharset());</span> -<span class="source-line-no">452</span><span id="line-452"> }</span> -<span class="source-line-no">453</span><span id="line-453"></span> -<span class="source-line-no">454</span><span id="line-454"> /**</span> -<span class="source-line-no">455</span><span id="line-455"> * Gets the contents of the file as a String, using the specified encoding. This method uses {@link #get()} to retrieve the contents of the file.</span> -<span class="source-line-no">456</span><span id="line-456"> *</span> -<span class="source-line-no">457</span><span id="line-457"> * @param charset The charset to use.</span> -<span class="source-line-no">458</span><span id="line-458"> * @return The contents of the file, as a string.</span> +<span class="source-line-no">113</span><span id="line-113"> /**</span> +<span class="source-line-no">114</span><span id="line-114"> * Default content charset to be used when no explicit charset parameter is provided by the sender. Media subtypes of the "text" type are defined to have a</span> +<span class="source-line-no">115</span><span id="line-115"> * default charset value of "ISO-8859-1" when received via HTTP.</span> +<span class="source-line-no">116</span><span id="line-116"> */</span> +<span class="source-line-no">117</span><span id="line-117"> public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;</span> +<span class="source-line-no">118</span><span id="line-118"></span> +<span class="source-line-no">119</span><span id="line-119"> /**</span> +<span class="source-line-no">120</span><span id="line-120"> * UID used in unique file name generation.</span> +<span class="source-line-no">121</span><span id="line-121"> */</span> +<span class="source-line-no">122</span><span id="line-122"> private static final String UID = UUID.randomUUID().toString().replace('-', '_');</span> +<span class="source-line-no">123</span><span id="line-123"></span> +<span class="source-line-no">124</span><span id="line-124"> /**</span> +<span class="source-line-no">125</span><span id="line-125"> * Counter used in unique identifier generation.</span> +<span class="source-line-no">126</span><span id="line-126"> */</span> +<span class="source-line-no">127</span><span id="line-127"> private static final AtomicInteger COUNTER = new AtomicInteger();</span> +<span class="source-line-no">128</span><span id="line-128"></span> +<span class="source-line-no">129</span><span id="line-129"> /**</span> +<span class="source-line-no">130</span><span id="line-130"> * Constructs a new {@link Builder}.</span> +<span class="source-line-no">131</span><span id="line-131"> *</span> +<span class="source-line-no">132</span><span id="line-132"> * @return a new {@link Builder}.</span> +<span class="source-line-no">133</span><span id="line-133"> */</span> +<span class="source-line-no">134</span><span id="line-134"> public static Builder builder() {</span> +<span class="source-line-no">135</span><span id="line-135"> return new Builder();</span> +<span class="source-line-no">136</span><span id="line-136"> }</span> +<span class="source-line-no">137</span><span id="line-137"></span> +<span class="source-line-no">138</span><span id="line-138"> /**</span> +<span class="source-line-no">139</span><span id="line-139"> * Tests if the file name is valid. For example, if it contains a NUL characters, it's invalid. If the file name is valid, it will be returned without any</span> +<span class="source-line-no">140</span><span id="line-140"> * modifications. Otherwise, throw an {@link InvalidPathException}.</span> +<span class="source-line-no">141</span><span id="line-141"> *</span> +<span class="source-line-no">142</span><span id="line-142"> * @param fileName The file name to check</span> +<span class="source-line-no">143</span><span id="line-143"> * @return Unmodified file name, if valid.</span> +<span class="source-line-no">144</span><span id="line-144"> * @throws InvalidPathException The file name is invalid.</span> +<span class="source-line-no">145</span><span id="line-145"> */</span> +<span class="source-line-no">146</span><span id="line-146"> public static String checkFileName(final String fileName) {</span> +<span class="source-line-no">147</span><span id="line-147"> if (fileName != null) {</span> +<span class="source-line-no">148</span><span id="line-148"> // Specific NUL check to build a better exception message.</span> +<span class="source-line-no">149</span><span id="line-149"> final var indexOf0 = fileName.indexOf(0);</span> +<span class="source-line-no">150</span><span id="line-150"> if (indexOf0 != -1) {</span> +<span class="source-line-no">151</span><span id="line-151"> final var sb = new StringBuilder();</span> +<span class="source-line-no">152</span><span id="line-152"> for (var i = 0; i < fileName.length(); i++) {</span> +<span class="source-line-no">153</span><span id="line-153"> final var c = fileName.charAt(i);</span> +<span class="source-line-no">154</span><span id="line-154"> switch (c) {</span> +<span class="source-line-no">155</span><span id="line-155"> case 0:</span> +<span class="source-line-no">156</span><span id="line-156"> sb.append("\\0");</span> +<span class="source-line-no">157</span><span id="line-157"> break;</span> +<span class="source-line-no">158</span><span id="line-158"> default:</span> +<span class="source-line-no">159</span><span id="line-159"> sb.append(c);</span> +<span class="source-line-no">160</span><span id="line-160"> break;</span> +<span class="source-line-no">161</span><span id="line-161"> }</span> +<span class="source-line-no">162</span><span id="line-162"> }</span> +<span class="source-line-no">163</span><span id="line-163"> throw new InvalidPathException(fileName, sb.toString(), indexOf0);</span> +<span class="source-line-no">164</span><span id="line-164"> }</span> +<span class="source-line-no">165</span><span id="line-165"> // Throws InvalidPathException on invalid file names</span> +<span class="source-line-no">166</span><span id="line-166"> Paths.get(fileName);</span> +<span class="source-line-no">167</span><span id="line-167"> }</span> +<span class="source-line-no">168</span><span id="line-168"> return fileName;</span> +<span class="source-line-no">169</span><span id="line-169"> }</span> +<span class="source-line-no">170</span><span id="line-170"></span> +<span class="source-line-no">171</span><span id="line-171"> /**</span> +<span class="source-line-no">172</span><span id="line-172"> * Gets an identifier that is unique within the class loader used to load this class, but does not have random-like appearance.</span> +<span class="source-line-no">173</span><span id="line-173"> *</span> +<span class="source-line-no">174</span><span id="line-174"> * @return A String with the non-random looking instance identifier.</span> +<span class="source-line-no">175</span><span id="line-175"> */</span> +<span class="source-line-no">176</span><span id="line-176"> private static String getUniqueId() {</span> +<span class="source-line-no">177</span><span id="line-177"> final var limit = 100_000_000;</span> +<span class="source-line-no">178</span><span id="line-178"> final var current = COUNTER.getAndIncrement();</span> +<span class="source-line-no">179</span><span id="line-179"> var id = Integer.toString(current);</span> +<span class="source-line-no">180</span><span id="line-180"></span> +<span class="source-line-no">181</span><span id="line-181"> // If you manage to get more than 100 million of ids, you'll</span> +<span class="source-line-no">182</span><span id="line-182"> // start getting ids longer than 8 characters.</span> +<span class="source-line-no">183</span><span id="line-183"> if (current < limit) {</span> +<span class="source-line-no">184</span><span id="line-184"> id = ("00000000" + id).substring(id.length());</span> +<span class="source-line-no">185</span><span id="line-185"> }</span> +<span class="source-line-no">186</span><span id="line-186"> return id;</span> +<span class="source-line-no">187</span><span id="line-187"> }</span> +<span class="source-line-no">188</span><span id="line-188"></span> +<span class="source-line-no">189</span><span id="line-189"> /**</span> +<span class="source-line-no">190</span><span id="line-190"> * The name of the form field as provided by the browser.</span> +<span class="source-line-no">191</span><span id="line-191"> */</span> +<span class="source-line-no">192</span><span id="line-192"> private String fieldName;</span> +<span class="source-line-no">193</span><span id="line-193"></span> +<span class="source-line-no">194</span><span id="line-194"> /**</span> +<span class="source-line-no">195</span><span id="line-195"> * The content type passed by the browser, or {@code null} if not defined.</span> +<span class="source-line-no">196</span><span id="line-196"> */</span> +<span class="source-line-no">197</span><span id="line-197"> private final String contentType;</span> +<span class="source-line-no">198</span><span id="line-198"></span> +<span class="source-line-no">199</span><span id="line-199"> /**</span> +<span class="source-line-no">200</span><span id="line-200"> * Whether or not this item is a simple form field.</span> +<span class="source-line-no">201</span><span id="line-201"> */</span> +<span class="source-line-no">202</span><span id="line-202"> private volatile boolean isFormField;</span> +<span class="source-line-no">203</span><span id="line-203"></span> +<span class="source-line-no">204</span><span id="line-204"> /**</span> +<span class="source-line-no">205</span><span id="line-205"> * The original file name in the user's file system.</span> +<span class="source-line-no">206</span><span id="line-206"> */</span> +<span class="source-line-no">207</span><span id="line-207"> private final String fileName;</span> +<span class="source-line-no">208</span><span id="line-208"></span> +<span class="source-line-no">209</span><span id="line-209"> /**</span> +<span class="source-line-no">210</span><span id="line-210"> * The size of the item, in bytes. This is used to cache the size when a file item is moved from its original location.</span> +<span class="source-line-no">211</span><span id="line-211"> */</span> +<span class="source-line-no">212</span><span id="line-212"> private volatile long size = -1;</span> +<span class="source-line-no">213</span><span id="line-213"></span> +<span class="source-line-no">214</span><span id="line-214"> /**</span> +<span class="source-line-no">215</span><span id="line-215"> * The threshold above which uploads will be stored on disk.</span> +<span class="source-line-no">216</span><span id="line-216"> */</span> +<span class="source-line-no">217</span><span id="line-217"> private final int threshold;</span> +<span class="source-line-no">218</span><span id="line-218"></span> +<span class="source-line-no">219</span><span id="line-219"> /**</span> +<span class="source-line-no">220</span><span id="line-220"> * The directory in which uploaded files will be stored, if stored on disk.</span> +<span class="source-line-no">221</span><span id="line-221"> */</span> +<span class="source-line-no">222</span><span id="line-222"> private final Path repository;</span> +<span class="source-line-no">223</span><span id="line-223"></span> +<span class="source-line-no">224</span><span id="line-224"> /**</span> +<span class="source-line-no">225</span><span id="line-225"> * Cached contents of the file.</span> +<span class="source-line-no">226</span><span id="line-226"> */</span> +<span class="source-line-no">227</span><span id="line-227"> private byte[] cachedContent;</span> +<span class="source-line-no">228</span><span id="line-228"></span> +<span class="source-line-no">229</span><span id="line-229"> /**</span> +<span class="source-line-no">230</span><span id="line-230"> * Output stream for this item.</span> +<span class="source-line-no">231</span><span id="line-231"> */</span> +<span class="source-line-no">232</span><span id="line-232"> private DeferredFileOutputStream dfos;</span> +<span class="source-line-no">233</span><span id="line-233"></span> +<span class="source-line-no">234</span><span id="line-234"> /**</span> +<span class="source-line-no">235</span><span id="line-235"> * The temporary file to use.</span> +<span class="source-line-no">236</span><span id="line-236"> */</span> +<span class="source-line-no">237</span><span id="line-237"> private final Path tempFile;</span> +<span class="source-line-no">238</span><span id="line-238"></span> +<span class="source-line-no">239</span><span id="line-239"> /**</span> +<span class="source-line-no">240</span><span id="line-240"> * The file items headers.</span> +<span class="source-line-no">241</span><span id="line-241"> */</span> +<span class="source-line-no">242</span><span id="line-242"> private FileItemHeaders fileItemHeaders;</span> +<span class="source-line-no">243</span><span id="line-243"></span> +<span class="source-line-no">244</span><span id="line-244"> /**</span> +<span class="source-line-no">245</span><span id="line-245"> * Default content Charset to be used when no explicit Charset parameter is provided by the sender.</span> +<span class="source-line-no">246</span><span id="line-246"> */</span> +<span class="source-line-no">247</span><span id="line-247"> private Charset charsetDefault = DEFAULT_CHARSET;</span> +<span class="source-line-no">248</span><span id="line-248"></span> +<span class="source-line-no">249</span><span id="line-249"> /**</span> +<span class="source-line-no">250</span><span id="line-250"> * Constructs a new {@code DiskFileItem} instance.</span> +<span class="source-line-no">251</span><span id="line-251"> *</span> +<span class="source-line-no">252</span><span id="line-252"> * @param fieldName The name of the form field.</span> +<span class="source-line-no">253</span><span id="line-253"> * @param contentType The content type passed by the browser or {@code null} if not specified.</span> +<span class="source-line-no">254</span><span id="line-254"> * @param isFormField Whether or not this item is a plain form field, as opposed to a file upload.</span> +<span class="source-line-no">255</span><span id="line-255"> * @param fileName The original file name in the user's file system, or {@code null} if not specified.</span> +<span class="source-line-no">256</span><span id="line-256"> * @param threshold The threshold, in bytes, below which items will be retained in memory and above which they will be stored as a file.</span> +<span class="source-line-no">257</span><span id="line-257"> * @param repository The data repository, which is the directory in which files will be created, should the item size exceed the threshold.</span> +<span class="source-line-no">258</span><span id="line-258"> * @param fileItemHeaders The file item headers.</span> +<span class="source-line-no">259</span><span id="line-259"> * @param defaultCharset The default Charset.</span> +<span class="source-line-no">260</span><span id="line-260"> */</span> +<span class="source-line-no">261</span><span id="line-261"> private DiskFileItem(final String fieldName, final String contentType, final boolean isFormField, final String fileName, final int threshold,</span> +<span class="source-line-no">262</span><span id="line-262"> final Path repository, final FileItemHeaders fileItemHeaders, final Charset defaultCharset) {</span> +<span class="source-line-no">263</span><span id="line-263"> this.fieldName = fieldName;</span> +<span class="source-line-no">264</span><span id="line-264"> this.contentType = contentType;</span> +<span class="source-line-no">265</span><span id="line-265"> this.charsetDefault = defaultCharset;</span> +<span class="source-line-no">266</span><span id="line-266"> this.isFormField = isFormField;</span> +<span class="source-line-no">267</span><span id="line-267"> this.fileName = fileName;</span> +<span class="source-line-no">268</span><span id="line-268"> this.fileItemHeaders = fileItemHeaders;</span> +<span class="source-line-no">269</span><span id="line-269"> this.threshold = threshold;</span> +<span class="source-line-no">270</span><span id="line-270"> this.repository = repository != null ? repository : PathUtils.getTempDirectory();</span> +<span class="source-line-no">271</span><span id="line-271"> this.tempFile = this.repository.resolve(String.format("upload_%s_%s.tmp", UID, getUniqueId()));</span> +<span class="source-line-no">272</span><span id="line-272"> }</span> +<span class="source-line-no">273</span><span id="line-273"></span> +<span class="source-line-no">274</span><span id="line-274"> /**</span> +<span class="source-line-no">275</span><span id="line-275"> * Deletes the underlying storage for a file item, including deleting any associated temporary disk file. This method can be used to ensure that this is</span> +<span class="source-line-no">276</span><span id="line-276"> * done at an earlier time, thus preserving system resources.</span> +<span class="source-line-no">277</span><span id="line-277"> *</span> +<span class="source-line-no">278</span><span id="line-278"> * @throws IOException if an error occurs.</span> +<span class="source-line-no">279</span><span id="line-279"> */</span> +<span class="source-line-no">280</span><span id="line-280"> @Override</span> +<span class="source-line-no">281</span><span id="line-281"> public DiskFileItem delete() throws IOException {</span> +<span class="source-line-no">282</span><span id="line-282"> cachedContent = null;</span> +<span class="source-line-no">283</span><span id="line-283"> final var outputFile = getPath();</span> +<span class="source-line-no">284</span><span id="line-284"> if (outputFile != null && !isInMemory() && Files.exists(outputFile)) {</span> +<span class="source-line-no">285</span><span id="line-285"> Files.delete(outputFile);</span> +<span class="source-line-no">286</span><span id="line-286"> }</span> +<span class="source-line-no">287</span><span id="line-287"> return this;</span> +<span class="source-line-no">288</span><span id="line-288"> }</span> +<span class="source-line-no">289</span><span id="line-289"></span> +<span class="source-line-no">290</span><span id="line-290"> /**</span> +<span class="source-line-no">291</span><span id="line-291"> * Gets the contents of the file as an array of bytes. If the contents of the file were not yet cached in memory, they will be loaded from the disk storage</span> +<span class="source-line-no">292</span><span id="line-292"> * and cached.</span> +<span class="source-line-no">293</span><span id="line-293"> *</span> +<span class="source-line-no">294</span><span id="line-294"> * @return The contents of the file as an array of bytes or {@code null} if the data cannot be read.</span> +<span class="source-line-no">295</span><span id="line-295"> * @throws IOException if an I/O error occurs.</span> +<span class="source-line-no">296</span><span id="line-296"> * @throws OutOfMemoryError See {@link Files#readAllBytes(Path)}: If an array of the required size cannot be allocated, for example the file is larger</span> +<span class="source-line-no">297</span><span id="line-297"> * that {@code 2GB}</span> +<span class="source-line-no">298</span><span id="line-298"> */</span> +<span class="source-line-no">299</span><span id="line-299"> @Override</span> +<span class="source-line-no">300</span><span id="line-300"> public byte[] get() throws IOException {</span> +<span class="source-line-no">301</span><span id="line-301"> if (isInMemory()) {</span> +<span class="source-line-no">302</span><span id="line-302"> if (cachedContent == null && dfos != null) {</span> +<span class="source-line-no">303</span><span id="line-303"> cachedContent = dfos.getData();</span> +<span class="source-line-no">304</span><span id="line-304"> }</span> +<span class="source-line-no">305</span><span id="line-305"> return cachedContent != null ? cachedContent.clone() : new byte[0];</span> +<span class="source-line-no">306</span><span id="line-306"> }</span> +<span class="source-line-no">307</span><span id="line-307"> return Files.readAllBytes(dfos.getFile().toPath());</span> +<span class="source-line-no">308</span><span id="line-308"> }</span> +<span class="source-line-no">309</span><span id="line-309"></span> +<span class="source-line-no">310</span><span id="line-310"> /**</span> +<span class="source-line-no">311</span><span id="line-311"> * Gets the content charset passed by the agent or {@code null} if not defined.</span> +<span class="source-line-no">312</span><span id="line-312"> *</span> +<span class="source-line-no">313</span><span id="line-313"> * @return The content charset passed by the agent or {@code null} if not defined.</span> +<span class="source-line-no">314</span><span id="line-314"> */</span> +<span class="source-line-no">315</span><span id="line-315"> public Charset getCharset() {</span> +<span class="source-line-no">316</span><span id="line-316"> final var parser = new ParameterParser();</span> +<span class="source-line-no">317</span><span id="line-317"> parser.setLowerCaseNames(true);</span> +<span class="source-line-no">318</span><span id="line-318"> // Parameter parser can handle null input</span> +<span class="source-line-no">319</span><span id="line-319"> final var params = parser.parse(getContentType(), ';');</span> +<span class="source-line-no">320</span><span id="line-320"> return Charsets.toCharset(params.get("charset"), charsetDefault);</span> +<span class="source-line-no">321</span><span id="line-321"> }</span> +<span class="source-line-no">322</span><span id="line-322"></span> +<span class="source-line-no">323</span><span id="line-323"> /**</span> +<span class="source-line-no">324</span><span id="line-324"> * Gets the default charset for use when no explicit charset parameter is provided by the sender.</span> +<span class="source-line-no">325</span><span id="line-325"> *</span> +<span class="source-line-no">326</span><span id="line-326"> * @return the default charset</span> +<span class="source-line-no">327</span><span id="line-327"> */</span> +<span class="source-line-no">328</span><span id="line-328"> public Charset getCharsetDefault() {</span> +<span class="source-line-no">329</span><span id="line-329"> return charsetDefault;</span> +<span class="source-line-no">330</span><span id="line-330"> }</span> +<span class="source-line-no">331</span><span id="line-331"></span> +<span class="source-line-no">332</span><span id="line-332"> /**</span> +<span class="source-line-no">333</span><span id="line-333"> * Gets the content type passed by the agent or {@code null} if not defined.</span> +<span class="source-line-no">334</span><span id="line-334"> *</span> +<span class="source-line-no">335</span><span id="line-335"> * @return The content type passed by the agent or {@code null} if not defined.</span> +<span class="source-line-no">336</span><span id="line-336"> */</span> +<span class="source-line-no">337</span><span id="line-337"> @Override</span> +<span class="source-line-no">338</span><span id="line-338"> public String getContentType() {</span> +<span class="source-line-no">339</span><span id="line-339"> return contentType;</span> +<span class="source-line-no">340</span><span id="line-340"> }</span> +<span class="source-line-no">341</span><span id="line-341"></span> +<span class="source-line-no">342</span><span id="line-342"> /**</span> +<span class="source-line-no">343</span><span id="line-343"> * Gets the name of the field in the multipart form corresponding to this file item.</span> +<span class="source-line-no">344</span><span id="line-344"> *</span> +<span class="source-line-no">345</span><span id="line-345"> * @return The name of the form field.</span> +<span class="source-line-no">346</span><span id="line-346"> * @see #setFieldName(String)</span> +<span class="source-line-no">347</span><span id="line-347"> */</span> +<span class="source-line-no">348</span><span id="line-348"> @Override</span> +<span class="source-line-no">349</span><span id="line-349"> public String getFieldName() {</span> +<span class="source-line-no">350</span><span id="line-350"> return fieldName;</span> +<span class="source-line-no">351</span><span id="line-351"> }</span> +<span class="source-line-no">352</span><span id="line-352"></span> +<span class="source-line-no">353</span><span id="line-353"> /**</span> +<span class="source-line-no">354</span><span id="line-354"> * Gets the file item headers.</span> +<span class="source-line-no">355</span><span id="line-355"> *</span> +<span class="source-line-no">356</span><span id="line-356"> * @return The file items headers.</span> +<span class="source-line-no">357</span><span id="line-357"> */</span> +<span class="source-line-no">358</span><span id="line-358"> @Override</span> +<span class="source-line-no">359</span><span id="line-359"> public FileItemHeaders getHeaders() {</span> +<span class="source-line-no">360</span><span id="line-360"> return fileItemHeaders;</span> +<span class="source-line-no">361</span><span id="line-361"> }</span> +<span class="source-line-no">362</span><span id="line-362"></span> +<span class="source-line-no">363</span><span id="line-363"> /**</span> +<span class="source-line-no">364</span><span id="line-364"> * Gets an {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file.</span> +<span class="source-line-no">365</span><span id="line-365"> *</span> +<span class="source-line-no">366</span><span id="line-366"> * @return An {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file.</span> +<span class="source-line-no">367</span><span id="line-367"> * @throws IOException if an error occurs.</span> +<span class="source-line-no">368</span><span id="line-368"> */</span> +<span class="source-line-no">369</span><span id="line-369"> @Override</span> +<span class="source-line-no">370</span><span id="line-370"> public InputStream getInputStream() throws IOException {</span> +<span class="source-line-no">371</span><span id="line-371"> if (!isInMemory()) {</span> +<span class="source-line-no">372</span><span id="line-372"> return Files.newInputStream(dfos.getFile().toPath());</span> +<span class="source-line-no">373</span><span id="line-373"> }</span> +<span class="source-line-no">374</span><span id="line-374"></span> +<span class="source-line-no">375</span><span id="line-375"> if (cachedContent == null) {</span> +<span class="source-line-no">376</span><span id="line-376"> cachedContent = dfos.getData();</span> +<span class="source-line-no">377</span><span id="line-377"> }</span> +<span class="source-line-no">378</span><span id="line-378"> return new ByteArrayInputStream(cachedContent);</span> +<span class="source-line-no">379</span><span id="line-379"> }</span> +<span class="source-line-no">380</span><span id="line-380"></span> +<span class="source-line-no">381</span><span id="line-381"> /**</span> +<span class="source-line-no">382</span><span id="line-382"> * Gets the original file name in the client's file system.</span> +<span class="source-line-no">383</span><span id="line-383"> *</span> +<span class="source-line-no">384</span><span id="line-384"> * @return The original file name in the client's file system.</span> +<span class="source-line-no">385</span><span id="line-385"> * @throws InvalidPathException The file name contains a NUL character, which might be an indicator of a security attack. If you intend to use the file name</span> +<span class="source-line-no">386</span><span id="line-386"> * anyways, catch the exception and use {@link InvalidPathException#getInput()}.</span> +<span class="source-line-no">387</span><span id="line-387"> */</span> +<span class="source-line-no">388</span><span id="line-388"> @Override</span> +<span class="source-line-no">389</span><span id="line-389"> public String getName() {</span> +<span class="source-line-no">390</span><span id="line-390"> return checkFileName(fileName);</span> +<span class="source-line-no">391</span><span id="line-391"> }</span> +<span class="source-line-no">392</span><span id="line-392"></span> +<span class="source-line-no">393</span><span id="line-393"> /**</span> +<span class="source-line-no">394</span><span id="line-394"> * Gets an {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file.</span> +<span class="source-line-no">395</span><span id="line-395"> *</span> +<span class="source-line-no">396</span><span id="line-396"> * @return An {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file.</span> +<span class="source-line-no">397</span><span id="line-397"> */</span> +<span class="source-line-no">398</span><span id="line-398"> @Override</span> +<span class="source-line-no">399</span><span id="line-399"> public OutputStream getOutputStream() {</span> +<span class="source-line-no">400</span><span id="line-400"> if (dfos == null) {</span> +<span class="source-line-no">401</span><span id="line-401"> dfos = DeferredFileOutputStream.builder().setThreshold(threshold).setOutputFile(getTempFile().toFile()).get();</span> +<span class="source-line-no">402</span><span id="line-402"> }</span> +<span class="source-line-no">403</span><span id="line-403"> return dfos;</span> +<span class="source-line-no">404</span><span id="line-404"> }</span> +<span class="source-line-no">405</span><span id="line-405"></span> +<span class="source-line-no">406</span><span id="line-406"> /**</span> +<span class="source-line-no">407</span><span id="line-407"> * Gets the {@link Path} for the {@code FileItem}'s data's temporary location on the disk. Note that for {@code FileItem}s that have their data stored in</span> +<span class="source-line-no">408</span><span id="line-408"> * memory, this method will return {@code null}. When handling large files, you can use {@link Files#move(Path,Path,CopyOption...)} to move the file to new</span> +<span class="source-line-no">409</span><span id="line-409"> * location without copying the data, if the source and destination locations reside within the same logical volume.</span> +<span class="source-line-no">410</span><span id="line-410"> *</span> +<span class="source-line-no">411</span><span id="line-411"> * @return The data file, or {@code null} if the data is stored in memory.</span> +<span class="source-line-no">412</span><span id="line-412"> */</span> +<span class="source-line-no">413</span><span id="line-413"> public Path getPath() {</span> +<span class="source-line-no">414</span><span id="line-414"> if (dfos == null) {</span> +<span class="source-line-no">415</span><span id="line-415"> return null;</span> +<span class="source-line-no">416</span><span id="line-416"> }</span> +<span class="source-line-no">417</span><span id="line-417"> if (isInMemory()) {</span> +<span class="source-line-no">418</span><span id="line-418"> return null;</span> +<span class="source-line-no">419</span><span id="line-419"> }</span> +<span class="source-line-no">420</span><span id="line-420"> return dfos.getFile().toPath();</span> +<span class="source-line-no">421</span><span id="line-421"> }</span> +<span class="source-line-no">422</span><span id="line-422"></span> +<span class="source-line-no">423</span><span id="line-423"> /**</span> +<span class="source-line-no">424</span><span id="line-424"> * Gets the size of the file.</span> +<span class="source-line-no">425</span><span id="line-425"> *</span> +<span class="source-line-no">426</span><span id="line-426"> * @return The size of the file, in bytes.</span> +<span class="source-line-no">427</span><span id="line-427"> */</span> +<span class="source-line-no">428</span><span id="line-428"> @Override</span> +<span class="source-line-no">429</span><span id="line-429"> public long getSize() {</span> +<span class="source-line-no">430</span><span id="line-430"> if (size >= 0) {</span> +<span class="source-line-no">431</span><span id="line-431"> return size;</span> +<span class="source-line-no">432</span><span id="line-432"> }</span> +<span class="source-line-no">433</span><span id="line-433"> if (cachedContent != null) {</span> +<span class="source-line-no">434</span><span id="line-434"> return cachedContent.length;</span> +<span class="source-line-no">435</span><span id="line-435"> }</span> +<span class="source-line-no">436</span><span id="line-436"> return dfos != null ? dfos.getByteCount() : 0;</span> +<span class="source-line-no">437</span><span id="line-437"> }</span> +<span class="source-line-no">438</span><span id="line-438"></span> +<span class="source-line-no">439</span><span id="line-439"> /**</span>
[... 30 lines stripped ...]