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

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

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


##########
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:
   Return `CharSequence` instead





> 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