View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.file;
16  
17  import static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
18  import static org.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
19  
20  import java.io.IOException;
21  import java.nio.charset.Charset;
22  import java.nio.charset.StandardCharsets;
23  import java.time.Instant;
24  import java.time.ZoneId;
25  import java.time.format.DateTimeFormatter;
26  
27  import org.apache.commons.io.FileUtils;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.htmlunit.BrowserVersion;
31  import org.htmlunit.corejs.javascript.Context;
32  import org.htmlunit.corejs.javascript.NativeArray;
33  import org.htmlunit.corejs.javascript.ScriptableObject;
34  import org.htmlunit.javascript.JavaScriptEngine;
35  import org.htmlunit.javascript.configuration.JsxClass;
36  import org.htmlunit.javascript.configuration.JsxConstructor;
37  import org.htmlunit.javascript.configuration.JsxFunction;
38  import org.htmlunit.javascript.configuration.JsxGetter;
39  import org.htmlunit.util.KeyDataPair;
40  
41  /**
42   * A JavaScript object for {@code File}.
43   *
44   * @author Ahmed Ashour
45   * @author Ronald Brill
46   */
47  @JsxClass
48  public class File extends Blob {
49  
50      private static final DateTimeFormatter LAST_MODIFIED_DATE_FORMATTER
51                              = DateTimeFormatter.ofPattern("EEE MMM dd yyyy HH:mm:ss 'GMT'Z");
52  
53      private static class FileBackend implements Backend {
54          private static final Log LOG = LogFactory.getLog(FileBackend.class);
55  
56          private final java.io.File file_;
57  
58          FileBackend(final String pathname) {
59              super();
60              file_ = new java.io.File(pathname);
61          }
62  
63          @Override
64          public String getName() {
65              return file_.getName();
66          }
67  
68          @Override
69          public long getLastModified() {
70              return file_.lastModified();
71          }
72  
73          @Override
74          public long getSize() {
75              return file_.length();
76          }
77  
78          @Override
79          public String getType(final BrowserVersion browserVersion) {
80              return browserVersion.getUploadMimeType(file_);
81          }
82  
83          @Override
84          public String getText() throws IOException {
85              return FileUtils.readFileToString(file_, StandardCharsets.UTF_8);
86          }
87  
88          @Override
89          public byte[] getBytes(final int start, final int end) {
90              final byte[] result = new byte[end - start];
91              try {
92                  System.arraycopy(FileUtils.readFileToByteArray(file_), start, result, 0, result.length);
93              }
94              catch (final IOException e) {
95                  LOG.error("FileBackend.getBytes failed", e);
96              }
97              return result;
98          }
99  
100         /**
101          * {@inheritDoc}
102          */
103         @Override
104         public KeyDataPair getKeyDataPair(final String name, final String fileName, final String contentType) {
105             return new KeyDataPair(name, file_, fileName, contentType, (Charset) null);
106         }
107     }
108 
109     /**
110      * Prototye ctor.
111      */
112     public File() {
113         super();
114     }
115 
116     /**
117      * Creates an instance.
118      * @param fileBits the bits
119      * @param fileName the Name
120      * @param properties the properties
121      * @throws IOException in case of error
122      */
123     @JsxConstructor
124     public void jsConstructor(final NativeArray fileBits, final String fileName,
125             final ScriptableObject properties) throws IOException {
126         if (fileBits == null
127                 || JavaScriptEngine.isUndefined(fileBits)
128                 || fileName == null
129                 || JavaScriptEngine.isUndefined(fileName)) {
130             throw JavaScriptEngine.typeError("Failed to construct 'File': 2 arguments required.");
131         }
132 
133         setBackend(InMemoryBackend.create(fileBits, fileName,
134                             extractFileTypeOrDefault(properties),
135                             extractLastModifiedOrDefault(properties)));
136     }
137 
138     File(final String pathname) {
139         super();
140         setBackend(new FileBackend(pathname));
141     }
142 
143     /**
144      * Returns the {@code name} property.
145      * @return the {@code name} property
146      */
147     @JsxGetter
148     public String getName() {
149         return getBackend().getName();
150     }
151 
152     /**
153      * Returns the {@code lastModifiedDate} property.
154      * @return the {@code lastModifiedDate} property
155      */
156     @JsxGetter({CHROME, EDGE})
157     public String getLastModifiedDate() {
158         final Context cx = Context.getCurrentContext();
159         final ZoneId zoneid = cx.getTimeZone().toZoneId();
160 
161         // strange only the time zone is locale dependent
162         String date = LAST_MODIFIED_DATE_FORMATTER.format(Instant.ofEpochMilli(getLastModified()).atZone(zoneid));
163         date += DateTimeFormatter.ofPattern(" (zzzz)", cx.getLocale())
164                     .format(Instant.ofEpochMilli(getLastModified()).atZone(zoneid));
165         return date;
166     }
167 
168     /**
169      * Returns the {@code lastModified} property.
170      * @return the {@code lastModified} property
171      */
172     @JsxGetter
173     public long getLastModified() {
174         return getBackend().getLastModified();
175     }
176 
177     /**
178      * Returns the {@code webkitRelativePath} property.
179      * @return the {@code webkitRelativePath} property
180      */
181     @JsxGetter
182     public String getWebkitRelativePath() {
183         return "";
184     }
185 
186     /**
187      * Slices the file.
188      */
189     @JsxFunction
190     public void slice() {
191         // nothing to do
192     }
193 }