View Javadoc
1   /*
2    * Copyright (c) 2002-2025 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 extends 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          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      */
122     @JsxConstructor
123     public void jsConstructor(final NativeArray fileBits, final String fileName, final ScriptableObject properties) {
124         if (fileBits == null
125                 || JavaScriptEngine.isUndefined(fileBits)
126                 || fileName == null
127                 || JavaScriptEngine.isUndefined(fileName)) {
128             throw JavaScriptEngine.typeError("Failed to construct 'File': 2 arguments required.");
129         }
130 
131         setBackend(InMemoryBackend.create(fileBits, fileName,
132                             extractFileTypeOrDefault(properties),
133                             extractLastModifiedOrDefault(properties)));
134     }
135 
136     File(final String pathname) {
137         super();
138         setBackend(new FileBackend(pathname));
139     }
140 
141     /**
142      * Returns the {@code name} property.
143      * @return the {@code name} property
144      */
145     @JsxGetter
146     public String getName() {
147         return getBackend().getName();
148     }
149 
150     /**
151      * Returns the {@code lastModifiedDate} property.
152      * @return the {@code lastModifiedDate} property
153      */
154     @JsxGetter({CHROME, EDGE})
155     public String getLastModifiedDate() {
156         final Context cx = Context.getCurrentContext();
157         final ZoneId zoneid = cx.getTimeZone().toZoneId();
158 
159         // strange only the time zone is locale dependent
160         String date = LAST_MODIFIED_DATE_FORMATTER.format(Instant.ofEpochMilli(getLastModified()).atZone(zoneid));
161         date += DateTimeFormatter.ofPattern(" (zzzz)", cx.getLocale())
162                     .format(Instant.ofEpochMilli(getLastModified()).atZone(zoneid));
163         return date;
164     }
165 
166     /**
167      * Returns the {@code lastModified} property.
168      * @return the {@code lastModified} property
169      */
170     @JsxGetter
171     public long getLastModified() {
172         return getBackend().getLastModified();
173     }
174 
175     /**
176      * Returns the {@code webkitRelativePath} property.
177      * @return the {@code webkitRelativePath} property
178      */
179     @JsxGetter
180     public String getWebkitRelativePath() {
181         return "";
182     }
183 
184     /**
185      * Slices the file.
186      */
187     @JsxFunction
188     public void slice() {
189         // nothing to do
190     }
191 }