我处于同样的情况:当我一次上传多个文件/在一个文件输入字段中时,我想从文件列表中删除一个特定文件。Wicket 6.x 中提供的“MultiFileUploadField”会删除所有文件,如果它们被一起选择的话,这不是我想要的。因为我正在为现有软件制作一种插件,所以我无法将 Wicket 升级到更新版本,也无法在 Wicket 应用程序中安装资源。
绝地先生在评论中提到:
据我所知,我使用了某种 jQuery uploder 插件,并使用隐藏输入做一些技巧来发送带有表单的文件。但我不确定,那是前一段时间,我没有项目来源。
我的解决方案还没有完成,并且没有使用上传插件(还没有?)。它可以用作使用 JavaScript 将文件上传到 Wicket 应用程序的“基础”。
关键组件是添加的ajaxBehavior,它接收由jQuery.ajax()提交的FormData 对象。
请注意, 我使用jQuery.ajax()将数据 POST 到 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 = {
"&" : "&",
"<" : "<",
">" : ">",
'"' : '"',
"'" : '''
};
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 尚未完全测试。当我发现问题时,我会发布脚本的更新。
您可能还想为 MultiFileUploadField 添加一个 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;
}
我MyMultiFileUploadField
在具有以下 HTML 的 Wicket 表单中使用。
<fieldset>
<legend>Upload form</legend>
<p>
<div wicket:id="fileInput" class="mfuex" />
</p>
<input wicket:id="submitUploads" type="submit" value="Upload"/>
</fieldset>