org.htmlunit.html.HtmlFileInput Maven / Gradle / Ivy
Show all versions of xlt Show documentation
/*
* Copyright (c) 2002-2023 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.htmlunit.html;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.htmlunit.SgmlPage;
import org.htmlunit.javascript.host.event.Event;
import org.htmlunit.util.KeyDataPair;
import org.htmlunit.util.MimeType;
import org.htmlunit.util.NameValuePair;
/**
* Wrapper for the HTML element "input".
*
* @author Mike Bowler
* @author Christian Sell
* @author Daniel Gredler
* @author Ahmed Ashour
* @author Marc Guillemot
* @author Frank Danek
* @author Ronald Brill
*/
public class HtmlFileInput extends HtmlInput implements LabelableElement {
private String contentType_;
private byte[] data_;
private File[] files_ = new File[0];
/**
* Creates an instance.
*
* @param qualifiedName the qualified name of the element type to instantiate
* @param page the page that contains this element
* @param attributes the initial attributes
*/
HtmlFileInput(final String qualifiedName, final SgmlPage page,
final Map attributes) {
super(qualifiedName, page, attributes);
final DomAttr valueAttrib = attributes.get("value");
if (valueAttrib != null) {
setDefaultValue(valueAttrib.getNodeValue());
}
}
/**
* Returns the in-memory data assigned to this file input element, if any.
* @return {@code null} if {@link #setData(byte[])} hasn't be used
*/
public final byte[] getData() {
return data_;
}
/**
* Assigns in-memory data to this file input element. During submission, instead
* of loading data from a file, the data is read from in-memory byte array.
*
* NOTE: Only use this method if you wish to upload in-memory data; if you instead
* wish to upload the contents of an actual file, use {@link #setValueAttribute(String)},
* passing in the path to the file.
*
* @param data the in-memory data assigned to this file input element
*/
public final void setData(final byte[] data) {
data_ = data;
}
/**
* {@inheritDoc}
*/
@Override
public void setDefaultChecked(final boolean defaultChecked) {
// Empty.
}
/**
* {@inheritDoc}
*/
@Override
public NameValuePair[] getSubmitNameValuePairs() {
if (files_ == null || files_.length == 0) {
return new NameValuePair[] {new KeyDataPair(getNameAttribute(), null, null, null, (Charset) null)};
}
final List list = new ArrayList<>();
for (final File file : files_) {
String contentType;
if (contentType_ == null) {
contentType = getPage().getWebClient().getBrowserVersion().getUploadMimeType(file);
if (StringUtils.isEmpty(contentType)) {
contentType = MimeType.APPLICATION_OCTET_STREAM;
}
}
else {
contentType = contentType_;
}
final Charset charset = getPage().getCharset();
final KeyDataPair keyDataPair = new KeyDataPair(getNameAttribute(), file, null, contentType, charset);
keyDataPair.setData(data_);
list.add(keyDataPair);
}
return list.toArray(new NameValuePair[0]);
}
/**
* Sets the content type value that should be sent together with the uploaded file.
* If content type is not explicitly set, HtmlUnit will try to guess it from the file content.
* @param contentType the content type ({@code null} resets it)
*/
public void setContentType(final String contentType) {
contentType_ = contentType;
}
/**
* Gets the content type that should be sent together with the uploaded file.
* @return the content type, or {@code null} if this has not been explicitly set
* and should be guessed from file content
*/
public String getContentType() {
return contentType_;
}
/**
* {@inheritDoc}
*/
@Override
public String getValue() {
final File[] files = getFiles();
if (files == null || files.length == 0) {
return ATTRIBUTE_NOT_DEFINED;
}
final File first = files[0];
final String name = first.getName();
if (name.isEmpty()) {
return name;
}
return "C:\\fakepath\\" + name;
}
/**
* {@inheritDoc}
*
* @see SubmittableElement#setDefaultValue(String)
*/
@Override
public void setDefaultValue(final String defaultValue) {
final String oldDefaultValue = getDefaultValue();
// overwritten because we have the overwritten setValueAttribute()
super.setValueAttribute(defaultValue);
if (oldDefaultValue.equals(getValue())) {
setRawValue(defaultValue);
}
}
/**
* {@inheritDoc}
* @deprecated as of version 3.0.0; use {@link #setValue(String)} instead
*/
@Deprecated
@Override
public void setValueAttribute(final String newValue) {
// make sure to remove setDefaultValue() also when removing this
super.setValueAttribute(newValue);
if (StringUtils.isEmpty(newValue)) {
files_ = new File[0];
return;
}
files_ = new File[] {normalizeFile(new File(newValue))};
}
/**
* {@inheritDoc}
*/
@Override
public void setValue(final String newValue) {
if (StringUtils.isEmpty(newValue)) {
setFiles();
return;
}
final File file = new File(newValue);
if (file.isDirectory()) {
setDirectory(file);
return;
}
setFiles(file);
}
/**
* Used to specify {@code multiple} files to upload.
*
* We may follow WebDriver solution, once made,
* see https://code.google.com/p/selenium/issues/detail?id=2239
* @param files the list of files to upload
*/
public void setFiles(final File... files) {
if (files.length > 1 && ATTRIBUTE_NOT_DEFINED == getAttributeDirect("multiple")) {
throw new IllegalStateException("HtmlFileInput - 'multiple' is not set.");
}
for (int i = 0; i < files.length; i++) {
files[i] = normalizeFile(files[i]);
}
files_ = files;
fireEvent(Event.TYPE_CHANGE);
}
/**
* Used to specify the upload directory.
*
* @param directory the directory to upload all files
*/
public void setDirectory(final File directory) {
if (directory == null) {
return;
}
if (ATTRIBUTE_NOT_DEFINED == getAttributeDirect("webkitdirectory")) {
throw new IllegalStateException("HtmlFileInput - 'webkitdirectory' is not set.");
}
if (ATTRIBUTE_NOT_DEFINED == getAttributeDirect("multiple")) {
throw new IllegalStateException("HtmlFileInput - 'multiple' is not set.");
}
if (!directory.isDirectory()) {
throw new IllegalStateException("HtmlFileInput - the provided directory '"
+ directory.getAbsolutePath() + "' is not a directory.");
}
final Collection fileColl = FileUtils.listFiles(directory, null, true);
final File[] files = new File[fileColl.size()];
int i = 0;
for (final File file : fileColl) {
files[i++] = normalizeFile(file);
}
files_ = files;
fireEvent(Event.TYPE_CHANGE);
}
/**
* To tolerate {@code file://}
*/
private static File normalizeFile(final File file) {
File f = null;
String path = file.getPath().replace('\\', '/');
if (path.startsWith("file:/")) {
if (path.startsWith("file://") && !path.startsWith("file:///")) {
path = "file:///" + path.substring(7);
}
try {
f = new File(new URI(path));
}
catch (final URISyntaxException e) {
// nothing here
}
}
if (f == null) {
f = new File(path);
}
return f;
}
/**
* Returns the files.
* @return the array of {@link File}s
*/
public File[] getFiles() {
return files_;
}
/**
* Returns whether this element satisfies all form validation constraints set.
* @return whether this element satisfies all form validation constraints set
*/
@Override
public boolean isValid() {
return isCustomValidityValid()
&& (!isRequiredSupported()
|| ATTRIBUTE_NOT_DEFINED == getAttributeDirect("required")
|| files_.length > 0);
}
}