使用 wicket ajax post 将文件数组从 js 发送到 java

Sending file array from js to java using wicket ajax post

本文关键字:js java 数组 ajax wicket post 文件 使用      更新时间:2023-09-26

我正在使用文件类型的字段输入加载多个文件列表。问题是我想在表单发布之前从原始列表中删除其中一些。 Couse 文件列表在 js 中是不可变的,我无法创建新的输入来发布被覆盖的文件列表(js 安全原因( 我必须使用我要提交的文件构建数组。

但是我不知道如何使用 Wicket.Ajax.post 发布和接收它(由于上述我无法发布表单(

标准文件上传字段获取请求作为表单帖子上的IMultipartWebRequest的实例。如何使用 Wicket.Ajax.post 做同样的事情?

Wicket.Ajax.post(( 是 http://api.jquery.com/jquery.ajax/的包装器。它只是给你一些钩子,你可以在其中操作请求或响应:onBefore,onPrecondition,onSuccess等。因此,如果你找到一种方法来做你需要的普通jQuery,那么只需在onBeforeSend钩子中添加这个逻辑。

你已经可以使用MultiFileUpload来做到这一点了?您不需要自己构建它。

请参阅此检票口示例:http://www.wicket-library.com/wicket-examples/upload/multi

我遇到了同样的情况:当我一次/在一个文件输入字段中上传多个文件时,我想从文件列表中删除一个特定文件。Wicket 6.x 中提供的"MultiFileUploadField"会删除所有文件,如果它们一起被选中,这不是我想要的。因为我正在为现有软件制作一种插件,所以我无法将 Wicket 升级到较新版本,也无法在 Wicket 应用程序中挂载资源。

绝地先生在评论中提到:

据我所知,我使用了某种jQuery更新插件,并使用隐藏的输入做了一些技巧,以发送带有表单的文件。但我不确定,那是前段时间,我没有项目来源。

我的解决方案尚未完成,并且不使用上传插件(还?它可以用作使用 JavaScript 将文件上传到 Wicket 应用程序的"基础"。
关键组件是添加的 ajaxBehavior,它接收由 jQuery.ajax(( 提交的 FormData 对象

需要注意的是,我使用 jQuery.ajax(( 将数据发布到 Wicket 应用程序,无需回退。
这可能不适用于较旧的浏览器。查看 jQuery 浏览器支持以获取更多信息。
另请注意,并非所有浏览器都完全支持 FormData 对象。

首先,我扩展了现有的MultiFileUploadField:

public class MyMultiFileUploadField extends MultiFileUploadField {
    private static final long serialVersionUID = 1L;
    private static final ResourceReference JS = new JavaScriptResourceReference(
                    MyMultiFileUploadField.class, "MyMultiFileUploadField.js");
    private final WebComponent upload;
    private final WebMarkupContainer container;
    private final AbstractAjaxBehavior ajaxBehavior;
    private final String componentIdPrefix;
    private final int maxUploads;
    private final boolean useMultipleAttr;

我们提供了自己的构造函数,在其中将 ajaxBehavior 添加到组件中:

public MyMultiFileUploadField(String id, IModel<? extends Collection<FileUpload>> model, int max,
                    boolean useMultipleAttr) {
        super(id, model, max);
        this.maxUploads = max;
        this.useMultipleAttr = useMultipleAttr;
        upload = (WebComponent) get("upload"); // Created by parent as: new WebComponent("upload");
        container = (WebMarkupContainer) get("container"); // Created by parent as: new WebMarkupContainer("container");
        ajaxBehavior = new MyMultiFileUploadBehavior();
        add(ajaxBehavior);
    }

还要重写 renderHead 方法,以便我们可以渲染我们自己的 JavaScript。
这里重要的是我们提供脚本的ajaxBehavior.getCallbackUrl()

 @Override
    public void renderHead(IHeaderResponse response) {
        response.render(JavaScriptHeaderItem.forReference(JS));
        response.render(OnDomReadyHeaderItem.forScript("new MultiFileUpload('" + getInputName() + "', document.getElementById('"
                        + container.getMarkupId() + "'), " + maxUploads + ", " + useMultipleAttr + ", '" + ajaxBehavior.getCallbackUrl()
                        + "').addElement(document.getElementById('" + upload.getMarkupId() + "'));"));
    } // new MultiFileUpload(uploadFieldName, uploadContainer, maxUploads, useMultipleAttr, callbackUrl).addElement(fileInput);

ajaxBehavior 将接收文件并将它们传递给 FileHandler 类进行保存。
非常感谢David Tanzer在他的帖子jQuery Wicket中对此进行了解释。

import org.apache.wicket.behavior.AbstractAjaxBehavior;
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.handler.TextRequestHandler;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.upload.FileItem;
import org.apache.wicket.util.upload.FileUploadException;
public class MyMultiFileUploadBehavior extends AbstractAjaxBehavior {
    private static final long serialVersionUID = 1L;
    @Override
    public void onRequest() {
        final RequestCycle requestCycle = RequestCycle.get();
        readRequest(requestCycle);
        sendResponse(requestCycle);
    }
    private void readRequest(final RequestCycle requestCycle) {
        Map<String, List<FileItem>> multiPartRequestFiles = null;
        final ServletWebRequest webRequest = (ServletWebRequest) requestCycle.getRequest();
        try {
            MultipartServletWebRequest multiPartRequest = webRequest.newMultipartWebRequest(Bytes.megabytes(1), "UploadInfo");
            multiPartRequest.parseFileParts();
            multiPartRequestFiles = multiPartRequest.getFiles();
        } catch (FileUploadException e) {
            e.printStackTrace(System.out);
            return;
        }
        if (multiPartRequestFiles != null && !multiPartRequestFiles.isEmpty()) {
            for (Entry<String, List<FileItem>> entry : multiPartRequestFiles.entrySet()) {
                // For debug: iterate over the map and print a list of filenames
                final String name = entry.getKey();
                System.out.println("Entry name: '" + name + "'");
                final List<FileItem> fileItems = entry.getValue();
                for (FileItem file : fileItems) {
                    System.out.println("Entry file: '" + file.getName() + "'");
                }
                List<FileUpload> fileUploads = buildFileUploadList(fileItems);
                FileUploadForm.getUploadFileHandler().persistFiles(fileUploads);
            }
        }
    }
    private void sendResponse(final RequestCycle requestCycle) {
        requestCycle.scheduleRequestHandlerAfterCurrent(
            new TextRequestHandler("application/json", "UTF-8", "[]"));
    }
    private List<FileUpload> buildFileUploadList(List<FileItem> fileItems) {
        List<FileUpload> fileUploads = new ArrayList<>(fileItems.size());
        for (FileItem fileItem : fileItems) {
            fileUploads.add(new FileUpload(fileItem));
        }
        return fileUploads;
    }
}

您可以按照 Wicket 示例中所示的相同方式保留文件(RobAU 也提到过(。

至于JavaScript,我基于Stickman制作的Wicket 6.x附带的脚本。请注意,此脚本仍然非常基本。
有关将 wicket abstractajaxbehavior 与 jquery ajax 一起使用的更多详细信息。
有关使用 jquery ajax 发送多部分表单数据的更多信息。

    /**
     * @author Stickman -- http://the-stickman.com
     * @author Vertongen
     * @see /org/apache/wicket/markup/html/form/upload/MultiFileUploadField.js
     */
function MultiFileUpload(uploadFieldName, uploadContainer, maxUploads, useMultipleAttr, callbackUrl) {
    "use strict";
    console.log("Params: " + uploadFieldName+ ", " + uploadContainer + ", " + maxUploads + ", " + useMultipleAttr + ", " + callbackUrl);
    // Is there a maximum?
    if (!maxUploads) {
        maxUploads = -1;
    }
    // Map to hold selected files. Key is formatted as: 'upload_' + uploadId
    var formDataMap = new Map();
    //this.formDataMap = formDataMap;
    var uploadId = 0;
    // Reference to the file input element
    var fileInputElement = null;
    /**
     * Add a new file input element
     */
    this.addElement = function(fileInput) {
        // Make sure it's a file input element
        if (fileInput.tagName.toLowerCase() === 'input' && fileInput.type.toLowerCase() === 'file') {
            if (useMultipleAttr) {
                fileInput.multiple = useMultipleAttr;
                if (Wicket && Wicket.Browser.isOpera()) {
                    // in Opera 12.02, changing 'multiple' this way
                    // does not update the field
                    fileInput.type = 'button';
                    fileInput.type = 'file';
                }
            }
            // Keep a reference to this MultiFileUpload object
            fileInput.multiFileUpload = this;
            // Keep a reference to the file input element
            fileInputElement = fileInput;
            // What to do when a file is selected
            fileInput.onchange = function() {
                // Check to see if we don't exceed the max.
                if (maxUploads !== -1) {
                    if (this.files.length > maxUploads) {
                        console.warn("More files selected than allowed!");
                        this.value = "";
                        return;
                    }
                    if((this.files.length + formDataMap.size) > maxUploads) {
                        console.warn("Total amount of files for upload exceeds the maximum!");
                        this.value = "";
                        return;
                    }
                }
                // Put selected files in the FormDataMap
                for (var i = 0, numFiles = this.files.length; i < numFiles; i++) {
                    uploadId++;
                    var fileId = "upload_" + uploadId;
                    var fileObj = this.files[i];
                    formDataMap.set(fileId, fileObj);
                    // Update uploadContainer add filenames to the list
                    this.multiFileUpload.addFileToUploadContainer(fileId, fileObj);
                }
                // Clear file input
                this.value = "";
                // If we've reached maximum number, disable file input element
                if (maxUploads !== -1 && formDataMap.size >= maxUploads) {
                    this.disabled = true;
                }           
            };          
        } else if (Wicket && Wicket.Log) {
            Wicket.Log.error('Error: not a file input element');
        }
    };
    this.addFileToUploadContainer = function(fileId, fileObj) {
        // Row div
        var new_row = document.createElement('tr');
        var contentsColumn = document.createElement('td');
        var buttonColumn = document.createElement('td');
        // Delete button
        var new_row_button = document.createElement('input');
        new_row_button.id = fileId;
        new_row_button.type = 'button';
        new_row_button.value = 'Remove';
        // Delete function
        new_row_button.onclick = function() {
            // Remove the selected file from the formData map.
            formDataMap.delete(this.id);
            // Remove this row from the list
            this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
            // Re-enable file input element (if it's disabled)
            fileInputElement.disabled = false;
            // Appease Safari
            //    without it Safari wants to reload the browser window
            //    which nixes your already queued uploads
            return false;
        };
        // Add filename and button to row
        contentsColumn.innerHTML = this.getOnlyFileName(fileObj.name);
        new_row.appendChild(contentsColumn);
        new_row_button.style.marginLeft = '20px';
        buttonColumn.appendChild(new_row_button);
        new_row.appendChild(buttonColumn);
        uploadContainer.appendChild(new_row);
    };
    var submitButton = document.getElementById('submitUploads');
    var resetButton = document.getElementById('resetUploads');
    var success = function() {console.log('success!'); };
    var failure = function() {console.log('failure.'); };
    var complete = function() {console.log('Done.'); };
    submitButton.onclick = function() {
        if(!formDataMap || formDataMap.size < 1) {
            console.warn("No files selected, cancelled upload!");
            return;
        }
        // Convert the Map into a FormData object.
        var formData = new FormData();
        formDataMap.forEach(function(value, key) {
              console.log(key + ' = ' + value);
              formData.append("uploads", value);
        });
        // Send the FormData object to our Wicket app.
        jQuery.ajax({
            url: callbackUrl,
            type: 'POST',
            data: formData,
            context: self,
            cache: false,
            processData: false,
            contentType: false,
            success: [success],
            error: [failure],
            // complete: [complete]
        });
    };
    resetButton.onclick = function() {
        formDataMap.clear();
        fileInputElement.disabled = false;
    };
    this.getOnlyFileName = function(file) {
        var toEscape = {
            "&" : "&amp;",
            "<" : "&lt;",
            ">" : "&gt;",
            '"' : '&quot;',
            "'" : '&#39;'
        };
        function replaceChar(ch) {
            return toEscape[ch] || ch;
        }
        function htmlEscape(fileName) {
            return fileName.replace(/[&<>'"]/g, replaceChar);
        }
        var separatorIndex1 = file.lastIndexOf('''');
        var separatorIndex2 = file.lastIndexOf('/');
        separatorIndex1 = Math.max(separatorIndex1, separatorIndex2);
        var fileName = separatorIndex1 >= 0 ? file.slice(separatorIndex1 + 1, file.length) : file;
        fileName = htmlEscape(fileName);
        return fileName;
    };
}

JavaScript 还没有完全测试。当我发现问题时,我会发布脚本的更新。

您可能还希望为多文件上传字段添加 HTML 副本。

<wicket:panel xmlns:wicket="http://wicket.apache.org">
    <input wicket:id="upload" type="file" class="wicket-mfu-field" />
    <div wicket:id="container" class="wicket-mfu-container">
        <div wicket:id="caption" class="wicket-mfu-caption"></div>
    </div>
</wicket:panel>

要使用MyMultiFileUploadField类,您可以查看 Wicket 示例(RobAU 也提到了(。下面的代码和 HTML 基于 Wicket 示例。

// collection that will hold uploaded FileUpload objects
private final Collection<FileUpload> uploads = new ArrayList<>();
public FileUploadForm(String formId, MultiUploadConfig multiUploadConfig) {
        super(formId);
        // set this form to multipart mode (always needed for uploads!)
        setMultiPart(true);
        // Add one multi-file upload field with this class attribute "uploads" as model
        multiFileUploadField = new MyMultiFileUploadField("fileInput",
                        new PropertyModel<Collection<FileUpload>>(this, "uploads"),
                        multiUploadConfig.getMaxNumberOfFiles(), true);
        add(multiFileUploadField);
        // Set the maximum size for uploads
        setMaxSize(Bytes.megabytes(multiUploadConfig.getMaxUploadSize()));
        // Set maximum size of each file in upload request
        setFileMaxSize(Bytes.megabytes(multiUploadConfig.getMaxFileSize()));
    }
    public static IUploadFileHandler getUploadFileHandler() {
        return _uploadFileHandler;
    }
    public static void setUploadFileHandler(IUploadFileHandler uploadFileHandler) {
        _uploadFileHandler = uploadFileHandler;
    }

我在具有以下HTML的Wicket形式中使用MyMultiFileUploadField

<fieldset>
  <legend>Upload form</legend>
    <p>
      <div wicket:id="fileInput" class="mfuex" />
    </p>
    <input wicket:id="submitUploads" type="submit" value="Upload"/>
</fieldset>