Error handling issues with multiple file uploads in Wicket: FileNotFoundException

We are encountering a unique issue in our Wicket 6+ application related to the FileUpload.writeTo(file) method. This error only occurs with specific file types (docx, xlsx, java) within one particular form. Interestingly, the identical code functions correctly for attaching files in other locations. Any assistance or insights would be greatly appreciated. Thank you.

HTML:

<div wicket:id="filesInput"/>

Java:

add(new org.apache.wicket.markup.html.form.upload.MultiFileUploadField("filesInput", 
                    new PropertyModel<Collection<FileUpload>>(this, "uploads"), 5));

The error occurs specifically on the line fileUpload.writeTo(file); when invoked from the form's onSubmit method.

private void writeToFile(FileUpload fileUpload, Attachment attachment) {
    if (fileUpload == null) {
        return;
    }
    File file = AttachmentUtils.getFile(attachment, jtracHome);
    try {
        fileUpload.writeTo(file);
    }
    catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Error Log:

[Insert log information here]

Answer №1

Here's where the issue arises in the code:

if (!outputFile.renameTo(file))
        {
            BufferedInputStream reader = null;
            BufferedOutputStream writer = null;
            try
            {
                reader = new BufferedInputStream(new FileInputStream(outputFile));
                writer = new BufferedOutputStream(new FileOutputStream(file));
                Streams.copy(reader, writer);
            }
            finally
            {
                IOUtils.closeQuietly(reader);
                IOUtils.closeQuietly(writer);
            }
        }

javadoc:

This method will only work for the first time it is invoked for a specific item. This is because if the method renames a temporary file, that file will no longer be available for copying or renaming later.

The error is occurring when attempting to open the "outputFile" File - indicating that it may no longer exist. It's possible that renameTo successfully did something with the file, but still returned false. Try setting a breakpoint there to investigate further. Could it be that the method is being called twice?

Regarding renameTo's javadoc:

Various aspects of this method's behavior depend on the platform: The rename operation may not be able to move a file across filesystems, may not be atomic, and may fail if a file with the destination path already exists. Always check the return value to ensure the rename operation was successful.

Answer №2

After running the wicket MultiUploadPage example, it's great to see everything functioning smoothly. Can you provide more insight into your page structure and what sets it apart?

WicketApplication.java

public class WicketApplication extends WebApplication {

    private Folder uploadFolder = null;

    @Override
    public Class<? extends WebPage> getHomePage(){
        return HomePage.class;
    }

    public Folder getUploadFolder(){
        return uploadFolder;
    }

    @Override
    public void init(){
        super.init();
        getResourceSettings().setThrowExceptionOnMissingResource(false);
        uploadFolder = new Folder(System.getProperty("java.io.tmpdir"), "wicket-uploads");
        uploadFolder.mkdirs();
        getApplicationSettings().setUploadProgressUpdatesEnabled(true);
    }
}

HomePage.java

public class HomePage extends WebPage {

private static final long serialVersionUID = 1L;
private final FileListView fileListView;

public HomePage(final PageParameters parameters){
    Folder uploadFolder = getUploadFolder();

    final FeedbackPanel uploadFeedback = new FeedbackPanel("uploadFeedback");
    add(uploadFeedback);

    final FileUploadForm simpleUploadForm = new FileUploadForm("simpleUpload");
    add(simpleUploadForm);

    add(new Label("dir", uploadFolder.getAbsolutePath()));
    fileListView = new FileListView("fileList", new LoadableDetachableModel<List<File>>(){

        private static final long serialVersionUID = 1L;

        @Override
        protected List<File> load(){
            return Arrays.asList(getUploadFolder().listFiles());
        }
    });
    add(fileListView);
}

private class FileListView extends ListView<File>{
    private static final long serialVersionUID = 1L;

    public FileListView(String name, final IModel<List<File>> files){
        super(name, files);
    }

    @Override
    protected void populateItem(ListItem<File> listItem){
        final File file = listItem.getModelObject();
        listItem.add(new Label("file", file.getName()));
        listItem.add(new Link<Void>("delete"){
            private static final long serialVersionUID = 1L;

            @Override
            public void onClick(){
                Files.remove(file);
                info("Deleted " + file);
            }
        });
    }
}

private class FileUploadForm extends Form<Void>{
    private static final long serialVersionUID = 1L;
    private final Collection<FileUpload> uploads = new ArrayList<FileUpload>();

    public FileUploadForm(String name){
        super(name);
        setMultiPart(true);
        add(new MultiFileUploadField("fileInput", new PropertyModel<Collection<FileUpload>>(this, "uploads"), 5, true));
        setMaxSize(Bytes.kilobytes(100000));
    }

    @Override
    protected void onSubmit(){
        for (FileUpload upload : uploads){
            File newFile = new File(getUploadFolder(), upload.getClientFileName());
            try{
                newFile.createNewFile();
                upload.writeTo(newFile);
                HomePage.this.info("saved file: " + upload.getClientFileName());
            } catch (Exception e){
                throw new IllegalStateException("Unable to write file");
            }
        }
    }
}

private Folder getUploadFolder(){
    return ((WicketApplication) Application.get()).getUploadFolder();
}

}

HomePage.html

    <!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
    <head>
        <meta charset="utf-8" />
        <title>Apache Wicket Quickstart</title>
        <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="Stylesheet" />
    </head>
    <body>
        Running the multi upload field has been successful thanks to the javascript discovered at
        <a target="_blank" href="http://the-stickman.com/web-development/javascript/upload-multiple-files-with-a-single-file-element/">here</a>.
        <br />
        <br /> Wicket is capable of handling file uploads via AJAX as well, a feature covered in the AJAX
        examples section. However, this example does not include AJAX Functionality to maintain simplicity.
        <br />
        <br />

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

        <div><span wicket:id="uploadFeedback" /></div>

        <div>
            <h4>Current files in <span wicket:id="dir">(dir)</span>:</h4>
            <table>
                <tr wicket:id="fileList">
                    <td width="200"><span wicket:id="file">(file)</span></td>
                    <td><a href="#" wicket:id="delete"><img src="delete.gif" border="0" /></a></td>
                </tr>
            </table>
        </div>
    </body>
</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Collaborate and apply coding principles across both Android and web platforms

Currently, I am developing a web version for my Android app. Within the app, there are numerous utility files such as a class that formats strings in a specific manner. I am wondering if there is a way to write this functionality once and use it on both ...

Is it possible to interchange the positions of two components in a routing system?

driver-details.component.ts @Component({ selector: 'app-driver-details', templateUrl: './driver-details.component.html', styleUrls: ['./driver-details.component.css'] }) export class DriverDetailsComponent implements OnI ...

Spring Boot fails to recognize path variable data sent from Angular

When working with Angular 7, I encountered a situation where I needed to pass a value from the service class of Angular. Here is how I achieved it: executeHelloWorldBeanServiceWithPathVariable(name){ console.log("name coming from here"+name); retu ...

Tips for modifying an axios instance during response interception

Is there a way to automatically update an axios instance with the latest token received in a response, without making a second request? The new token can be included in any response after any request, and I want to make sure that the last received token ...

Developing J2EE servlets with Angular for HTTP POST requests

I've exhausted my search on Google and tried numerous PHP workarounds to no avail. My issue lies in attempting to send POST parameters to a j2ee servlet, as the parameters are not being received at the servlet. Strangely though, I can successfully rec ...

Enhance your AJAX calls with jQuery by confidently specifying the data type of successful responses using TypeScript

In our development process, we implement TypeScript for type hinting in our JavaScript code. Type hinting is utilized for Ajax calls as well to define the response data format within the success callback. This exemplifies how it could be structured: inter ...