[ 
https://issues.apache.org/jira/browse/WICKET-7033?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17710919#comment-17710919
 ] 

ASF GitHub Bot commented on WICKET-7033:
----------------------------------------

martin-g commented on code in PR #571:
URL: https://github.com/apache/wicket/pull/571#discussion_r1162678493


##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/AbstractFileUploadResource.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadBase;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.lang.Bytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.github.openjson.JSONObject;
+
+/**
+ * The resource that handles the file uploads.
+ * Reads the file items from the request parameters and uses {@link 
IUploadsFileManager}

Review Comment:
   s/IUploadsFileManager/IFileUploadManager/ ?



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/AbstractFileUploadResource.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadBase;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.lang.Bytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.github.openjson.JSONObject;
+
+/**
+ * The resource that handles the file uploads.
+ * Reads the file items from the request parameters and uses {@link 
IUploadsFileManager}
+ * to store them.
+ * Additionally, cares about the response's content type and body.
+ * <p>
+ * This code was adapted from
+ * <p>
+ * <a 
href="https://github.com/martin-g/blogs/blob/master/file-upload/src/main/java/com/mycompany/fileupload/AbstractFileUploadResource.java";>AbstractFileUploadResource.java</a>
+ * <p>
+ * The main difference is that there some JQuery plugin is used at client side 
(and it supports multiple uploads +
+ * some UI allowing to delete/preview files and so on).
+ * Here we are just using plain jQuery code at client side to upload a single 
file.
+ */
+public abstract class AbstractFileUploadResource extends AbstractResource
+{
+       private static final Logger LOG = 
LoggerFactory.getLogger(AbstractFileUploadResource.class);
+
+       public static final String PARAM_NAME = "FILE-UPLOAD";
+
+       private final IUploadsFileManager fileManager;
+
+       public AbstractFileUploadResource(IUploadsFileManager fileManager)
+       {
+               this.fileManager = fileManager;
+       }
+
+       /**
+        * Reads and stores the uploaded files
+        * 
+        * @param attributes
+        *            Attributes
+        * @return ResourceResponse
+        */
+       @Override
+       protected ResourceResponse newResourceResponse(Attributes attributes)
+       {
+               final ResourceResponse resourceResponse = new 
ResourceResponse();
+
+               final ServletWebRequest webRequest = (ServletWebRequest) 
attributes.getRequest();
+
+               String identifier = 
webRequest.getRequestParameters().getParameterValue("uploadId").toString("resource");
+
+               try
+               {
+                       MultipartServletWebRequest multiPartRequest = 
webRequest.newMultipartWebRequest(getMaxSize(), identifier);
+                       multiPartRequest.parseFileParts();
+
+                       RequestCycle.get().setRequest(multiPartRequest);
+
+                       Map<String, List<FileItem>> files = 
multiPartRequest.getFiles();
+                       List<FileItem> fileItems = files.get(PARAM_NAME);
+
+                       if (fileItems != null)
+                       {
+                               List<FileUpload> fileUploads = new 
ArrayList<>();
+                               for (FileItem fileItem : fileItems)
+                               {
+                                       fileUploads.add(new 
FileUpload(fileItem));
+                               }
+                               saveFiles(fileUploads, identifier);
+                               prepareResponse(resourceResponse, webRequest, 
fileUploads);
+                       }
+                       else
+                       {
+                               
resourceResponse.setContentType("application/json");
+                               resourceResponse.setWriteCallback(new 
WriteCallback()
+                               {
+                                       @Override
+                                       public void writeData(Attributes 
attributes) throws IOException
+                                       {
+                                               JSONObject json = new 
JSONObject();
+                                               json.put("error", true);
+                                               json.put("errorMessage", "No 
files selected!");
+                                               String error = json.toString();
+                                               
attributes.getResponse().write(error);
+                                       }
+                               });
+                       }
+
+               }
+               catch (FileUploadException e)
+               {
+                       if (e instanceof 
FileUploadBase.FileSizeLimitExceededException || e instanceof 
FileUploadBase.SizeLimitExceededException)
+                       {
+                               resourceResponse.setError(413, "File size 
exceeded!");
+                               return resourceResponse;
+                       }
+                       throw new WicketRuntimeException(e);
+               }
+               catch (Exception fux)
+               {
+                       LOG.error("An error occurred while uploading a file", 
fux);
+                       resourceResponse.setContentType("application/json");
+                       JSONObject json = new JSONObject();
+                       json.put("error", true);
+                       json.put("errorMessage", fux.getMessage());

Review Comment:
   We should probably use some generic error message instead.
   Use the real exception message only if `LOGGER.isDebugEnabled() == true`



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/AbstractFileUploadResource.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadBase;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.lang.Bytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.github.openjson.JSONObject;
+
+/**
+ * The resource that handles the file uploads.
+ * Reads the file items from the request parameters and uses {@link 
IUploadsFileManager}
+ * to store them.
+ * Additionally, cares about the response's content type and body.
+ * <p>
+ * This code was adapted from
+ * <p>
+ * <a 
href="https://github.com/martin-g/blogs/blob/master/file-upload/src/main/java/com/mycompany/fileupload/AbstractFileUploadResource.java";>AbstractFileUploadResource.java</a>
+ * <p>
+ * The main difference is that there some JQuery plugin is used at client side 
(and it supports multiple uploads +
+ * some UI allowing to delete/preview files and so on).
+ * Here we are just using plain jQuery code at client side to upload a single 
file.
+ */
+public abstract class AbstractFileUploadResource extends AbstractResource
+{
+       private static final Logger LOG = 
LoggerFactory.getLogger(AbstractFileUploadResource.class);
+
+       public static final String PARAM_NAME = "FILE-UPLOAD";

Review Comment:
   ```suggestion
        public static final String PARAM_NAME = "WICKET-FILE-UPLOAD";
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadResourceReference.java:
##########
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.util.List;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Bytes;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONException;
+import com.github.openjson.JSONObject;
+
+/**
+ * A resource reference that provides default implementation of 
AbstractFileUploadResource.
+ * The implementation generates JSON response with data from the upload (this 
data is
+ * re-routed to page for things like getting the client file name and file 
size).
+ */
+public class FileUploadResourceReference extends ResourceReference
+{
+
+       private final IUploadsFileManager uploadFileManager;
+
+       private static FileUploadResourceReference i;

Review Comment:
   ```suggestion
        private static FileUploadResourceReference instance;
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);

Review Comment:
   The `file` might be `null`



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();

Review Comment:
   I am still not sure what this identifier is. I think it deserves some javadoc



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/AbstractFileUploadResource.java:
##########
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileUploadBase;
+import org.apache.commons.fileupload.FileUploadException;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.lang.Bytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.github.openjson.JSONObject;
+
+/**
+ * The resource that handles the file uploads.
+ * Reads the file items from the request parameters and uses {@link 
IUploadsFileManager}
+ * to store them.
+ * Additionally, cares about the response's content type and body.
+ * <p>
+ * This code was adapted from
+ * <p>
+ * <a 
href="https://github.com/martin-g/blogs/blob/master/file-upload/src/main/java/com/mycompany/fileupload/AbstractFileUploadResource.java";>AbstractFileUploadResource.java</a>
+ * <p>
+ * The main difference is that there some JQuery plugin is used at client side 
(and it supports multiple uploads +
+ * some UI allowing to delete/preview files and so on).
+ * Here we are just using plain jQuery code at client side to upload a single 
file.
+ */
+public abstract class AbstractFileUploadResource extends AbstractResource
+{
+       private static final Logger LOG = 
LoggerFactory.getLogger(AbstractFileUploadResource.class);
+
+       public static final String PARAM_NAME = "FILE-UPLOAD";
+
+       private final IUploadsFileManager fileManager;
+
+       public AbstractFileUploadResource(IUploadsFileManager fileManager)
+       {
+               this.fileManager = fileManager;
+       }
+
+       /**
+        * Reads and stores the uploaded files
+        * 
+        * @param attributes
+        *            Attributes
+        * @return ResourceResponse
+        */
+       @Override
+       protected ResourceResponse newResourceResponse(Attributes attributes)
+       {
+               final ResourceResponse resourceResponse = new 
ResourceResponse();
+
+               final ServletWebRequest webRequest = (ServletWebRequest) 
attributes.getRequest();
+
+               String identifier = 
webRequest.getRequestParameters().getParameterValue("uploadId").toString("resource");

Review Comment:
   Extract a constant for `"uploadId"` with some javadoc about what is its 
purpose



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";

Review Comment:
   final ?



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();

Review Comment:
   ```suggestion
           protected abstract List<UploadInfo> getFileUploadInfos();
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);
+                if (success) {
+                    String filesIfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("filesInfo").toString();
+                    fileInfos = UploadInfo.fromJson(filesIfo);
+                    onUploadSuccess(target, getFileInfos());
+                    if (deleteFilesAfterUpload())
+                    {
+                        for (UploadInfo uploadInfo : fileInfos)
+                        {
+                            fileManager().deleteFile(getMarkupId(), 
uploadInfo.clientFileName);
+                        }
+                    }
+                }
+                else
+                {
+                    String errorInfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("errorMessage").toString(null);
+                    if (UPLOAD_CANCELED.equals(errorInfo))
+                    {
+                        onUploadCanceled(target);
+                    } else {
+                        onUploadFailure(target, errorInfo);
+                    }
+                }
+            }
+        };
+        add(ajaxBehavior);
+    }
+
+    /**
+     * @return determines whether the file is deleted after upload has happened
+     * or not.
+     */
+    protected boolean deleteFilesAfterUpload()
+    {
+        return true;
+    }
+
+
+    /**
+     * @return determines whether the created files are deleted on remove
+     */
+    protected boolean clearFilesOnRemove()
+    {
+        return true;
+    }
+
+    private List<UploadInfo> getFileInfos()
+    {
+        return (List<UploadInfo>)getDefaultModel().getObject();
+    }
+
+    @Override
+    public FileUpload getFileUpload() {
+        throw new 
UnsupportedOperationException("SingleFileUploadToResourceField does not support 
working with FileUpload");
+    }
+
+    @Override
+    protected void onRemove()
+    {
+        super.onRemove();
+        // we clean any client side mess if component is removed via "partial" 
page replacement
+        AjaxUtils.executeIfAjaxOrWebSockets(target -> 
target.appendJavaScript("delete window." + getMarkupId() + ";"));

Review Comment:
   Do not pollute the `window` object.
   I think we have `Wicket.Timer` that could be used instead.



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);
+                if (success) {
+                    String filesIfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("filesInfo").toString();
+                    fileInfos = UploadInfo.fromJson(filesIfo);
+                    onUploadSuccess(target, getFileInfos());
+                    if (deleteFilesAfterUpload())
+                    {
+                        for (UploadInfo uploadInfo : fileInfos)
+                        {
+                            fileManager().deleteFile(getMarkupId(), 
uploadInfo.clientFileName);
+                        }
+                    }
+                }
+                else
+                {
+                    String errorInfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("errorMessage").toString(null);
+                    if (UPLOAD_CANCELED.equals(errorInfo))
+                    {
+                        onUploadCanceled(target);
+                    } else {
+                        onUploadFailure(target, errorInfo);
+                    }
+                }
+            }
+        };
+        add(ajaxBehavior);
+    }
+
+    /**
+     * @return determines whether the file is deleted after upload has happened
+     * or not.
+     */
+    protected boolean deleteFilesAfterUpload()
+    {
+        return true;
+    }
+
+
+    /**
+     * @return determines whether the created files are deleted on remove
+     */
+    protected boolean clearFilesOnRemove()
+    {
+        return true;
+    }
+
+    private List<UploadInfo> getFileInfos()
+    {
+        return (List<UploadInfo>)getDefaultModel().getObject();
+    }
+
+    @Override
+    public FileUpload getFileUpload() {
+        throw new 
UnsupportedOperationException("SingleFileUploadToResourceField does not support 
working with FileUpload");
+    }
+
+    @Override
+    protected void onRemove()
+    {
+        super.onRemove();
+        // we clean any client side mess if component is removed via "partial" 
page replacement
+        AjaxUtils.executeIfAjaxOrWebSockets(target -> 
target.appendJavaScript("delete window." + getMarkupId() + ";"));
+        if (clearFilesOnRemove())
+        {
+            fileManager().deleteFiles(getMarkupId());
+        }
+    }
+
+    /**
+     * Override to do something on a successful upload. Mind that if {@link 
#deleteFilesAfterUpload()} returns <code>true</code>
+     * then after this method is called the file will be deleted. Thus, it is 
your responsibility to store the file somewhere
+     * in this method.
+     *
+     * @param target         The {@link AjaxRequestTarget}
+     * @param fileInfos      The List<FileInfo
+     */
+    protected abstract void onUploadSuccess(AjaxRequestTarget target, 
List<UploadInfo> fileInfos);
+
+
+    /**
+     *  Override to do something on a non successful upload
+     *
+     * @param target The {@link AjaxRequestTarget}
+     * @param errorInfo The cause of the failure
+     */
+    protected void onUploadFailure(AjaxRequestTarget target, String errorInfo)
+    {
+        // nothing by default
+    }
+
+    /**
+     *  Override to do something in case user canceled upload
+     *
+     * @param target The {@link AjaxRequestTarget}
+     */
+    protected void onUploadCanceled(AjaxRequestTarget target)
+    {
+        // nothing by default
+    }
+
+    /**
+     * @return  a unique identifier for the user (session) doing the upload.
+     */
+    protected String getUserId()
+    {
+        return Session.get().getId() != null ? Session.get().getId() : "Annon";

Review Comment:
   s/Annon/Anon/
   
   But since we use Wicket Ajax behaviors is it possible at all to be anonymous 
?



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.
+        formData.append("uploadId", this.inputName);
+        var self = this;
+        // we use jQuery to post the file to the resource (this.resourceUrl)
+        // and we keep a reference to the request in order to be able
+        // to cancel the upload
+        this.xhr = $.ajax({
+            url: this.resourceUrl,
+            type: "POST",
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (res) {
+                // do clean up on success
+                self.clientSideSuccessCallBack();

Review Comment:
   shouldn't this call be in the `else` clause ?



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);
+                if (success) {
+                    String filesIfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("filesInfo").toString();
+                    fileInfos = UploadInfo.fromJson(filesIfo);
+                    onUploadSuccess(target, getFileInfos());
+                    if (deleteFilesAfterUpload())
+                    {
+                        for (UploadInfo uploadInfo : fileInfos)
+                        {
+                            fileManager().deleteFile(getMarkupId(), 
uploadInfo.clientFileName);
+                        }
+                    }
+                }
+                else
+                {
+                    String errorInfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("errorMessage").toString(null);
+                    if (UPLOAD_CANCELED.equals(errorInfo))
+                    {
+                        onUploadCanceled(target);
+                    } else {
+                        onUploadFailure(target, errorInfo);
+                    }
+                }
+            }
+        };
+        add(ajaxBehavior);
+    }
+
+    /**
+     * @return determines whether the file is deleted after upload has happened
+     * or not.
+     */
+    protected boolean deleteFilesAfterUpload()
+    {
+        return true;
+    }
+
+
+    /**
+     * @return determines whether the created files are deleted on remove
+     */
+    protected boolean clearFilesOnRemove()
+    {
+        return true;
+    }
+
+    private List<UploadInfo> getFileInfos()
+    {
+        return (List<UploadInfo>)getDefaultModel().getObject();
+    }
+
+    @Override
+    public FileUpload getFileUpload() {
+        throw new 
UnsupportedOperationException("SingleFileUploadToResourceField does not support 
working with FileUpload");
+    }
+
+    @Override
+    protected void onRemove()
+    {
+        super.onRemove();
+        // we clean any client side mess if component is removed via "partial" 
page replacement
+        AjaxUtils.executeIfAjaxOrWebSockets(target -> 
target.appendJavaScript("delete window." + getMarkupId() + ";"));
+        if (clearFilesOnRemove())
+        {
+            fileManager().deleteFiles(getMarkupId());
+        }
+    }
+
+    /**
+     * Override to do something on a successful upload. Mind that if {@link 
#deleteFilesAfterUpload()} returns <code>true</code>
+     * then after this method is called the file will be deleted. Thus, it is 
your responsibility to store the file somewhere
+     * in this method.
+     *
+     * @param target         The {@link AjaxRequestTarget}
+     * @param fileInfos      The List<FileInfo
+     */
+    protected abstract void onUploadSuccess(AjaxRequestTarget target, 
List<UploadInfo> fileInfos);
+
+
+    /**
+     *  Override to do something on a non successful upload
+     *
+     * @param target The {@link AjaxRequestTarget}
+     * @param errorInfo The cause of the failure
+     */
+    protected void onUploadFailure(AjaxRequestTarget target, String errorInfo)
+    {
+        // nothing by default
+    }
+
+    /**
+     *  Override to do something in case user canceled upload
+     *
+     * @param target The {@link AjaxRequestTarget}
+     */
+    protected void onUploadCanceled(AjaxRequestTarget target)
+    {
+        // nothing by default
+    }
+
+    /**
+     * @return  a unique identifier for the user (session) doing the upload.
+     */
+    protected String getUserId()
+    {
+        return Session.get().getId() != null ? Session.get().getId() : "Annon";
+    }
+
+
+    @Override
+    public void renderHead(IHeaderResponse response)
+    {
+        CoreLibrariesContributor.contributeAjax(getApplication(), response);
+        response.render(JavaScriptHeaderItem.forReference(JS));
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("inputName", getMarkupId());
+        jsonObject.put("resourceUrl", urlFor(getFileUploadResourceReference(), 
new PageParameters()).toString());
+        jsonObject.put("ajaxCallBackUrl", ajaxBehavior.getCallbackUrl());
+        response.render(OnDomReadyHeaderItem.forScript("window."
+                + getMarkupId() + " = new Wicket.FileUploadToResourceField("
+                + jsonObject + ","
+                + getClientSideSuccessCallBack() + "," + 
getClientSideCancelCallBack() + ","
+                + getConnectionErrorCallBack() + ");"));
+    }
+
+    /**
+     * Override if you need to return a different instance of 
FileUploadResourceReference
+     * @return FileUploadResourceReference
+     */
+    protected FileUploadResourceReference getFileUploadResourceReference()
+    {
+        return FileUploadResourceReference.getInstance();
+    }
+
+    /**
+     * @return The JavaScript expression starting the upload.
+     */
+    public String getTriggerUploadScript()
+    {
+        return "window." + getMarkupId() + ".upload();";
+    }
+
+    /**
+     * Starts the upload via an AJAX request.
+     *
+     * @param target The {@link AjaxRequestTarget}
+     */
+    public void startUpload(IPartialPageRequestHandler target)
+    {
+        target.appendJavaScript(getTriggerUploadScript());
+    }
+
+    /**
+     * @return The JavaScript expression canceling the upload.
+     */
+    public String getTriggerCancelUploadScript()
+    {
+        return "window." + getMarkupId() + ".cancel();";
+    }
+    /**
+     * Cancels the upload via an AJAX request.
+     *
+     * @param target The {@link AjaxRequestTarget}
+     */
+    public void cancelUpload(IPartialPageRequestHandler target)
+    {
+        target.appendJavaScript(getTriggerCancelUploadScript());
+    }
+
+    /**
+     * @return A JavaScript function to be executed on successful upload. This 
is, besides the normal wicket
+     * AJAX request (see {@link #onUploadSuccess(AjaxRequestTarget, List)}).
+     */
+    protected String getClientSideSuccessCallBack()

Review Comment:
   Are those clientside callbacks really needed ?
   I don't quite like the Strings they return ...



##########
wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadApplication.java:
##########
@@ -55,9 +61,22 @@ protected void init()
                // Ensure folder exists
                uploadFolder.mkdirs();
 
+               uploadsFileManager = new FolderUploadsFileManager(uploadFolder);
+
+               mountResource("/uploads", 
FileUploadResourceReference.createNewInstance(uploadsFileManager, 
Bytes.megabytes(100)));

Review Comment:
   100M is too big. We deploy the examples app at 
https://examples9x.wicket.apache.org and this may consume out disks quickly.



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadResourceReference.java:
##########
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.util.List;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Bytes;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONException;
+import com.github.openjson.JSONObject;
+
+/**
+ * A resource reference that provides default implementation of 
AbstractFileUploadResource.
+ * The implementation generates JSON response with data from the upload (this 
data is
+ * re-routed to page for things like getting the client file name and file 
size).
+ */
+public class FileUploadResourceReference extends ResourceReference
+{
+
+       private final IUploadsFileManager uploadFileManager;
+
+       private static FileUploadResourceReference i;
+
+       /**
+        * This method assumes {@link #createNewInstance(IUploadsFileManager, 
Bytes)} was called before

Review Comment:
   Why not merged the two methods ?
   `getInstance()` could do what `#createNewInstance()` does.



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.

Review Comment:
   ```
   This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}!
   The upload of the file id done via the {@link 
#startUpload(IPartialPageRequestHandler)} method.
    ```
   



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {

Review Comment:
   log it as debug ?!



##########
wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadToResourcePage.html:
##########
@@ -0,0 +1,39 @@
+<html xmlns:wicket="http://wicket.apache.org";>
+<head>
+    <title>Wicket Examples - upload</title>
+       <style>
+               legend { border: 1px solid #e9601a; background-color: #bbb; 
color: #fff; padding: 4px;}
+               fieldset { border: 1px solid #e9601a; padding: 10px; 
margin-top: 10px;}
+       </style>
+</head>
+<body>
+    <wicket:extend>
+
+       <p>Wicket can upload to a <strong>mounted to a resource.</strong></p>
+       <p>This upload is done via jQuery and does not block wicket AJAX</p>

Review Comment:
   ```suggestion
        <p>This upload is done via jQuery and does not block Wicket AJAX</p>
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.

Review Comment:
   ```suggestion
           // pass the input name to know where to store files at server side.
   ```



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/form/upload/progressbar.js:
##########
@@ -117,8 +135,14 @@
                        this.iframe = null;
 
                        if (progressPercent === '100') {
-                               Wicket.$(this.statusid).setAttribute('hidden', 
'');
-                               Wicket.$(this.barid).setAttribute('hidden', '');
+                               var $statusId = Wicket.$(this.statusid);
+                               if ($statusId != null) {
+                                       $statusId.setAttribute('hidden', '');

Review Comment:
   Why use `hidden` attribute ?
   Can we use Wicket.DOM's `hide()` and `show()` instead ?



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);

Review Comment:
   cache the value of `RequestCycle.get().getRequest().getRequestParameters()` 
and reuse it



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);
+                if (success) {
+                    String filesIfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("filesInfo").toString();
+                    fileInfos = UploadInfo.fromJson(filesIfo);
+                    onUploadSuccess(target, getFileInfos());
+                    if (deleteFilesAfterUpload())
+                    {
+                        for (UploadInfo uploadInfo : fileInfos)
+                        {
+                            fileManager().deleteFile(getMarkupId(), 
uploadInfo.clientFileName);
+                        }
+                    }
+                }
+                else
+                {
+                    String errorInfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("errorMessage").toString(null);
+                    if (UPLOAD_CANCELED.equals(errorInfo))
+                    {
+                        onUploadCanceled(target);
+                    } else {
+                        onUploadFailure(target, errorInfo);
+                    }
+                }
+            }
+        };
+        add(ajaxBehavior);
+    }
+
+    /**
+     * @return determines whether the file is deleted after upload has happened
+     * or not.
+     */
+    protected boolean deleteFilesAfterUpload()
+    {
+        return true;
+    }
+
+
+    /**
+     * @return determines whether the created files are deleted on remove
+     */
+    protected boolean clearFilesOnRemove()
+    {
+        return true;
+    }
+
+    private List<UploadInfo> getFileInfos()
+    {
+        return (List<UploadInfo>)getDefaultModel().getObject();
+    }
+
+    @Override
+    public FileUpload getFileUpload() {
+        throw new 
UnsupportedOperationException("SingleFileUploadToResourceField does not support 
working with FileUpload");

Review Comment:
   SingleFileUploadToResourceField - Single ?!



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.java:
##########
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.apache.commons.io.IOUtils;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.AjaxUtils;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.resource.CoreLibrariesContributor;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+/**
+ * Implementation of FileUploadField capable to uploading a file into a wicket 
mounted resource.
+ * This field does not require a {@link 
org.apache.wicket.markup.html.form.Form}, and field by itself
+ * does to upload of the file via the {@link 
#startUpload(IPartialPageRequestHandler)} method. See wicket examples.
+ */
+public abstract class FileUploadToResourceField extends FileUploadField
+{
+
+    /**
+     * Info regarding an upload.
+     */
+    public static final class UploadInfo
+    {
+        private File file;
+        private final String clientFileName;
+
+        private final long size;
+
+        private final String contentType;
+
+        public UploadInfo(String clientFileName, long size, String contentType)
+        {
+            this.clientFileName = clientFileName;
+            this.size = size;
+            this.contentType = contentType;
+        }
+
+        public File getFile()
+        {
+            return file;
+        }
+
+        public void setFile(File file)
+        {
+            this.file = file;
+        }
+
+        public String getClientFileName()
+        {
+            return clientFileName;
+        }
+
+        public long getSize()
+        {
+            return size;
+        }
+
+        public String getContentType()
+        {
+            return contentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UploadInfo fileInfo = (UploadInfo) o;
+            return Objects.equals(clientFileName, fileInfo.clientFileName);
+        }
+
+        /**
+         * @return the bytes associated with the upload.
+         */
+        public byte[] get()
+        {
+            byte[] fileData = new byte[(int) getSize()];
+            InputStream fis = null;
+
+            try
+            {
+                fis = new FileInputStream(file);
+                IOUtils.readFully(fis, fileData);
+            }
+            catch (IOException e)
+            {
+                fileData = null;
+            }
+            finally
+            {
+                IOUtils.closeQuietly(fis);
+            }
+
+            return fileData;
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Objects.hash(clientFileName);
+        }
+
+        public static List<UploadInfo> fromJson(String json)
+        {
+            List<UploadInfo> infos = new ArrayList<>();
+            JSONArray jsonArray = new JSONArray(json);
+            for (int i = 0; i < jsonArray.length(); i++)
+            {
+                JSONObject jsonObject = jsonArray.getJSONObject(i);
+                infos.add(new 
UploadInfo(jsonObject.getString("clientFileName"), jsonObject.getLong("size"), 
jsonObject.getString("contentType")));
+            }
+            return infos;
+        }
+    }
+
+    private static final JavaScriptResourceReference JS = new 
JavaScriptResourceReference(FileUploadToResourceField.class, 
"FileUploadToResourceField.js");
+
+    public static String UPLOAD_CANCELED = "upload.canceled";
+
+
+    private static abstract class FileModel implements IModel<List<UploadInfo>>
+    {
+
+        @Override
+        public List<UploadInfo> getObject()
+        {
+            List<UploadInfo> fileInfos = getFileInfos();
+            for (UploadInfo uploadInfo : fileInfos){
+                uploadInfo.setFile(fileManager().getFile(getIdentifier(), 
uploadInfo.clientFileName));
+            }
+            return fileInfos;
+        }
+
+        @Override
+        public void setObject(List<UploadInfo> object)
+        {
+            throw new UnsupportedOperationException("setObject not supported");
+        }
+
+        protected abstract IUploadsFileManager fileManager();
+
+        protected abstract String getIdentifier();
+        protected abstract List<UploadInfo> getFileInfos();
+    }
+
+    private final AbstractDefaultAjaxBehavior ajaxBehavior;
+
+    private transient List<UploadInfo> fileInfos;
+
+    public FileUploadToResourceField(String id) {
+        super(id);
+        setOutputMarkupId(true);
+        setMarkupId("WRFUF" + getUserId() + System.currentTimeMillis());
+        setDefaultModel(new FileModel() {
+            @Override
+            protected IUploadsFileManager fileManager() {
+                return FileUploadToResourceField.this.fileManager();
+            }
+
+            @Override
+            protected String getIdentifier()
+            {
+                return FileUploadToResourceField.this.getMarkupId();
+            }
+
+            @Override
+            protected List<UploadInfo> getFileInfos()
+            {
+                return fileInfos;
+            }
+        });
+        ajaxBehavior = new AbstractDefaultAjaxBehavior()
+        {
+            @Override
+            protected void respond(AjaxRequestTarget target)
+            {
+                boolean success = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("success").toBoolean(false);
+                if (success) {
+                    String filesIfo = 
RequestCycle.get().getRequest().getRequestParameters().getParameterValue("filesInfo").toString();
+                    fileInfos = UploadInfo.fromJson(filesIfo);
+                    onUploadSuccess(target, getFileInfos());
+                    if (deleteFilesAfterUpload())
+                    {
+                        for (UploadInfo uploadInfo : fileInfos)
+                        {
+                            fileManager().deleteFile(getMarkupId(), 
uploadInfo.clientFileName);

Review Comment:
   This looks scary!
   It seems I can provide specially crafted JSON with `filesInfo` and delete 
files on the server ?!



##########
wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadToResourcePage.html:
##########
@@ -0,0 +1,39 @@
+<html xmlns:wicket="http://wicket.apache.org";>
+<head>
+    <title>Wicket Examples - upload</title>
+       <style>
+               legend { border: 1px solid #e9601a; background-color: #bbb; 
color: #fff; padding: 4px;}
+               fieldset { border: 1px solid #e9601a; padding: 10px; 
margin-top: 10px;}
+       </style>
+</head>
+<body>
+    <wicket:extend>
+
+       <p>Wicket can upload to a <strong>mounted to a resource.</strong></p>

Review Comment:
   ```suggestion
        <p>Wicket can upload to a <strong>mounted resource.</strong></p>
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.apache.wicket.markup.html.form.upload.resource;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.util.file.File;
+import org.apache.wicket.util.io.IOUtils;
+import org.apache.wicket.util.lang.Args;
+
+public class FolderUploadsFileManager implements IUploadsFileManager

Review Comment:
   javadoc



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.
+        formData.append("uploadId", this.inputName);
+        var self = this;
+        // we use jQuery to post the file to the resource (this.resourceUrl)
+        // and we keep a reference to the request in order to be able
+        // to cancel the upload
+        this.xhr = $.ajax({
+            url: this.resourceUrl,
+            type: "POST",
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (res) {
+                // do clean up on success
+                self.clientSideSuccessCallBack();
+                if (res.error) {
+                    var ep = {'success': false, 'errorMessage': 
res.errorMessage};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack(res);
+                } else {
+                    var ep = {'success': true, 'filesInfo': 
JSON.stringify(res)};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                }
+            },
+            error: function (jqXHR, textStatus, errorThrown) {
+                if (textStatus === "abort") {
+                    // user aborted the upload.
+                    self.clientSideCancelCallBack();
+                    var ep = {'success': false, 'errorMessage': 
'upload.canceled'};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                } else if (textStatus === "error"){
+                    var ep = {'success': false, 'errorMessage': errorThrown};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack();
+                } else if (textStatus === "parsererror"){
+                    // this error will only happen is generated JSON at server 
side is faulty
+                    var data = jqXHR.responseText;
+                    Wicket.Log.log(data);
+                }
+            }
+        });
+    }
+
+    // cancel the upload
+    Wicket.FileUploadToResourceField.prototype.cancel = function () {
+        // we have a reference to the request we can cancel it.
+        if (this.xhr) {
+            this.xhr.abort();
+            Wicket.Log.log("upload canceled!");

Review Comment:
   ```suggestion
               Wicket.Log.log("the upload has been canceled!");
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.
+        formData.append("uploadId", this.inputName);
+        var self = this;
+        // we use jQuery to post the file to the resource (this.resourceUrl)
+        // and we keep a reference to the request in order to be able
+        // to cancel the upload
+        this.xhr = $.ajax({
+            url: this.resourceUrl,
+            type: "POST",
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (res) {
+                // do clean up on success
+                self.clientSideSuccessCallBack();
+                if (res.error) {
+                    var ep = {'success': false, 'errorMessage': 
res.errorMessage};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack(res);
+                } else {
+                    var ep = {'success': true, 'filesInfo': 
JSON.stringify(res)};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                }
+            },
+            error: function (jqXHR, textStatus, errorThrown) {
+                if (textStatus === "abort") {
+                    // user aborted the upload.
+                    self.clientSideCancelCallBack();
+                    var ep = {'success': false, 'errorMessage': 
'upload.canceled'};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                } else if (textStatus === "error"){
+                    var ep = {'success': false, 'errorMessage': errorThrown};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack();
+                } else if (textStatus === "parsererror"){
+                    // this error will only happen is generated JSON at server 
side is faulty
+                    var data = jqXHR.responseText;
+                    Wicket.Log.log(data);
+                }
+            }
+        });
+    }
+
+    // cancel the upload
+    Wicket.FileUploadToResourceField.prototype.cancel = function () {
+        // we have a reference to the request we can cancel it.
+        if (this.xhr) {
+            this.xhr.abort();
+            Wicket.Log.log("upload canceled!");
+            delete (this.xhr);
+        } else {
+            Wicket.Log.log("Too late to cancel: upload already finished.");

Review Comment:
   ```suggestion
               Wicket.Log.log("Too late to cancel: the upload has already 
finished.");
   ```



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.
+        formData.append("uploadId", this.inputName);
+        var self = this;
+        // we use jQuery to post the file to the resource (this.resourceUrl)
+        // and we keep a reference to the request in order to be able
+        // to cancel the upload
+        this.xhr = $.ajax({
+            url: this.resourceUrl,
+            type: "POST",
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (res) {
+                // do clean up on success
+                self.clientSideSuccessCallBack();
+                if (res.error) {
+                    var ep = {'success': false, 'errorMessage': 
res.errorMessage};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack(res);
+                } else {
+                    var ep = {'success': true, 'filesInfo': 
JSON.stringify(res)};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                }
+            },
+            error: function (jqXHR, textStatus, errorThrown) {
+                if (textStatus === "abort") {
+                    // user aborted the upload.
+                    self.clientSideCancelCallBack();
+                    var ep = {'success': false, 'errorMessage': 
'upload.canceled'};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                } else if (textStatus === "error"){
+                    var ep = {'success': false, 'errorMessage': errorThrown};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack();
+                } else if (textStatus === "parsererror"){
+                    // this error will only happen is generated JSON at server 
side is faulty
+                    var data = jqXHR.responseText;
+                    Wicket.Log.log(data);
+                }
+            }
+        });
+    }
+
+    // cancel the upload
+    Wicket.FileUploadToResourceField.prototype.cancel = function () {
+        // we have a reference to the request we can cancel it.
+        if (this.xhr) {
+            this.xhr.abort();
+            Wicket.Log.log("upload canceled!");
+            delete (this.xhr);
+        } else {
+            Wicket.Log.log("Too late to cancel: upload already finished.");
+        }

Review Comment:
   For easier debugging I'd suggest to use ` this.inputName ` in the log 
messages above.



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FileUploadToResourceField.js:
##########
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ *      http://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.
+ */
+
+;(function (undefined) {
+
+    'use strict';
+
+    if (typeof(Wicket.FileUploadToResourceField) === 'object') {
+        return;
+    }
+
+    Wicket.FileUploadToResourceField = function (settings, 
clientSideSuccessCallBack, clientSideCancelCallBack, connectionErrorCallBack)
+    {
+        this.inputName = settings.inputName;
+        this.input = document.getElementById(this.inputName);
+        this.resourceUrl = settings.resourceUrl + "?uploadId=" + 
this.inputName;
+        this.ajaxCallBackUrl = settings.ajaxCallBackUrl;
+        this.clientSideSuccessCallBack = clientSideSuccessCallBack;
+        this.clientSideCancelCallBack = clientSideCancelCallBack;
+        this.connectionErrorCallBack = connectionErrorCallBack;
+    }
+
+    Wicket.FileUploadToResourceField.prototype.upload = function()
+    {
+        // we add the files to a FormData object.
+        var formData = new FormData();
+        var totalfiles = this.input.files.length;
+        for (var index = 0; index < totalfiles; index++) {
+            formData.append("FILE-UPLOAD",this.input.files[index]);
+        }
+        // pass the input name to knw where to store files at server side.
+        formData.append("uploadId", this.inputName);
+        var self = this;
+        // we use jQuery to post the file to the resource (this.resourceUrl)
+        // and we keep a reference to the request in order to be able
+        // to cancel the upload
+        this.xhr = $.ajax({
+            url: this.resourceUrl,
+            type: "POST",
+            data: formData,
+            processData: false,
+            contentType: false,
+            success: function (res) {
+                // do clean up on success
+                self.clientSideSuccessCallBack();
+                if (res.error) {
+                    var ep = {'success': false, 'errorMessage': 
res.errorMessage};
+                    Wicket.Ajax.get({"u": self.ajaxCallBackUrl, "ep": ep});
+                    self.connectionErrorCallBack(res);

Review Comment:
   The Java method that provides the impl of `connectionErrorCallBack` should 
be documented that the function accepts `res` as argument.



##########
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/form/upload/UploadProgressBar.java:
##########
@@ -112,14 +112,38 @@ public void destroy(final Application application)
 
        private static final long serialVersionUID = 1L;
 
-       private final Form<?> form;
+       private Form<?> form;
 
        private MarkupContainer statusDiv;
 
        private MarkupContainer barDiv;
 
        private final FileUploadField uploadField;
 
+       /**
+        * Constructor that will display the upload progress bar for every 
submit of the given form.
+        *
+        * @param id
+        *            component id (not null)
+        * @param uploadField
+        *            the file upload field to check for a file upload, or null 
to display the upload
+        *            field for every submit of the given form
+        */
+       public UploadProgressBar(final String id,  final FileUploadField 
uploadField)
+       {
+               super(id);

Review Comment:
   You can call `this(id, null, Args.nonNull(uploadField, "uploadField"))`
   And move the non-null Form from 
https://github.com/apache/wicket/pull/571/files#diff-f75b5636ac8b2157552b9755243a10c168527c2132bd8ce61393386a49482047R183
 to 
https://github.com/apache/wicket/pull/571/files#diff-f75b5636ac8b2157552b9755243a10c168527c2132bd8ce61393386a49482047R157





> add support to uploading to a resource
> --------------------------------------
>
>                 Key: WICKET-7033
>                 URL: https://issues.apache.org/jira/browse/WICKET-7033
>             Project: Wicket
>          Issue Type: New Feature
>          Components: wicket
>            Reporter: Ernesto Reinaldo Barreiro
>            Assignee: Ernesto Reinaldo Barreiro
>            Priority: Major
>             Fix For: 10.0.0, 9.14.0
>
>
> Add support for the following:
> * Upload to a resource in an asynchronous non page blocking request
> * Add an optional way to block the user from leaving the page while the 
> upload is happening 
> * Ways to cancel the upload
> * Adapt the upload progress bar to work with this new "component" and improve 
> its code as in some corner cases it is producing client side errors (I 
> created an issue for that some time ago). 
> * Maybe useful too:  create a web socket based progress bar, as the upload 
> progress bar now works pulling the server every second. 
> * Also to add an example to wicket-examples that uses a smart JS uploader, 
> like in the blog 
> (https://github.com/martin-g/blogs/blob/master/file-upload/). This way you 
> will verify that the new APIs are easily extendable.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to