Remove file uploaded (vaadin-upload)

It’s possible to remove a file uploaded by user ?

When I upload a file, I insert a object into a list like this :

List<fileUploadDtoList> fileUploadDtoList =  new ArrayList();
uploadFileButton.addSucceededListener(event -> {
            InputStream inputStream = buffer.getInputStream(event.getFileName());

            if(inputStream != null) {
                FileUploadDto fileUploadDto = new FileUploadDto();
                fileUploadDto.setFileName(event.getFileName());
                fileUploadDto.setMimeType(event.getMIMEType());
                byte[] bytes = IOUtils.toByteArray(inputStream);
                fileUploadDto.setByteArrayDocument(bytes);
                fileUploadDtoList.add(fileUploadDto);
            }
        });
		

I want to remove the file from fileUploadDtoList when the user remove the file.
It’s possible to do somethink like that :

uploadFileButton.addRemoveFileListener(event -> {
    event.getFileName(); removeToFileUploadList();...
}

We have provided a solution to this problem although with some hacking. We use “file-remove” event
We create component and remove it in a listener

upload.addSucceededListener(event -> {

	if (component != null) {
		vlowerLayout.remove(component);

	}
	component = createComponent(event.getMIMEType(), event.getFileName(), buffer.getInputStream());
	if (component instanceof Image) {
		upload.getElement().addEventListener("file-remove", new DomEventListener() {
			@Override
			public void handleEvent(DomEvent arg0) {
				component.setVisible(false);
			}
		});
		// upload.getElement().addEventData("event.detail.file.name");
	}
	p = new HtmlComponent(Tag.H3);
	p.getElement().setText(event.getFileName());
	vlowerLayout.add(component);
});

it took us some hacking around but the key here is the “file-remove” event. As far as I remember
we could not get it to work with the other remove listeners

Thanks for the workaround proposal, I linked this discussion to GitHub ticket about the same issue: https://github.com/vaadin/vaadin-upload/issues/284

Tatu, please attach this thread as well

https://vaadin.com/forum/thread/17107628/typeerror-what-causes-them

We must fix the Upload component as even though we got it to work it is throwing exceptions
on the client side to the point that it scares users so bad that we may have to entirely remove it from use as totally unfit for production. Any idea how to choke JS exceptions it is throwing? (how to get them choked - those JS exception popups are truly ANNOYING - upload works for us other than that) see images below. upload works but those exceptions ruin any decent/acceptable user experience.

17362262.png
17362270.png

Hi thanks but it not answer to my question I think.

I want to remove a object from fileUploadDtoList
see below my code;
when user upload a file, I store into a list information like (fileName, mimeType, inputStream …)
and when user remove a file from multiple file uploaded, I want to remove the object from my list.
I want to upload any type of file ( pdf, html, png, jpeg).
I don’t want to createComponent or remove it.
why when user remove a file we can’t access to fileName removed ?
It’s possible to avoid multiple file with same name ?

private MultiFileMemoryBuffer buffer = new MultiFileMemoryBuffer();
private UploadButton uploadFileButton = new UploadButton(buffer);
private List<FileUploadDto> fileUploadDtoList = new ArrayList<>();

class UploadButton extends Upload {
        public UploadButton(MultiFileMemoryBuffer buffer) {
            super(buffer);
        }

        Registration addFileRemoveListener(ComponentEventListener<FileRemoveEvent> listener) {
            return super.addListener(FileRemoveEvent.class, listener);
        }
    }


uploadFileButton.addSucceededListener(event -> {
            InputStream inputStream = buffer.getInputStream(event.getFileName());

            if(inputStream != null) {
                FileUploadDto fileUploadDto = new FileUploadDto();
                fileUploadDto.setFileName(event.getFileName());
                fileUploadDto.setMimeType(event.getMIMEType());
                try {
                    byte[] bytes = IOUtils.toByteArray(inputStream);
                    fileUploadDto.setByteArrayDocument(bytes);
                    fileUploadDtoList.add(fileUploadDto);

                } catch (IOException exception) {
                    log.error(exception.getMessage());
                }
            }
        });
		

uploadFileButton.addFileRemoveListener( event -> {
            // TODO REMOVE FILE FROM fileUploadDtoList
        });
		

how about you try a hashmap where you index on filename and remove in this event based on file name
just grab more info from DomEvent

component = createComponent(event.getMIMEType(), event.getFileName(), buffer.getInputStream());
if (component instanceof Image) {
	uploadButton.getElement().addEventListener("file-remove", new DomEventListener() {
		@Override
		public void handleEvent(DomEvent arg0) {
			//here remove from hashmap based on filename
		}
	});
	
As i had mentioned I think  addRemoveLsitener is NOT working hence the workaround with addEventListener("file-remove"

Hey, I’m having the same issue as the OP. I use the multi-file Upload component and store the uploaded files in a HashSet. I’ve also created a clear button which empties said HashSet and also clears the Upload component’s file list. However, now I’m trying to actually handle the user clicking on the small cross button in order to only remove a specific file but I cannot figure out from the DomEvent itself which file the user removed.

Peter wrote in the post above that one should get the file name and use that to identify the file. But, looking at the object in the debugger, the DomEvent doesn’t seem to contain any information on the file name.

Update: another thing I just tried was getting the files Json Array directly and compare its content to my HashSet. However, that array seems to be always empty.

up.addFileRemoveListener(event -> {
	final Object raw = getUpload().getElement().getPropertyRaw("files"); // Always empty.
	// ...
});

Is there any update on this? Please tell me there is an update or at least that it’s being worked on. The github for vaadin-upload-flow looks like it has been abandoned some months ago. I can not use Upload component as long as it behaves like that. My customer would run away as fast as humanly possible.

I see several mentions of different workarounds for the removing of uploaded files, but all seem too complicated and I don’t really understand them.
Does anyone currently use the Upload, allowing multiple files to be uploaded? If so, can you show how you handled the removal of already uploaded files?

Here is my current solution with the drawback that when the user removes just one file using the cross button, it will remove all files. (As I don’t have the information which one the user intended to remove.)

My custom Upload component:

package de.fzi.pde.gui.view;

import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.upload.Receiver;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.shared.Registration;

/**
 * @author bender
 *
 */
public class PDEUpload extends Upload {
	
	/**
	 * @author bender
	 *
	 */
	@DomEvent("file-remove")
    public static class FileRemoveEvent extends ComponentEvent<Upload> {
		
		private static final long serialVersionUID = 2608319952827964746L;

		/**
		 * @param source
		 * @param fromClient
		 */
		public FileRemoveEvent(final Upload source, final boolean fromClient) {
			super(source, fromClient);
        }
    }

	private static final long serialVersionUID = 4432689815466357962L;

	/**
	 * Constr.
	 */
	public PDEUpload() {
		super();
	}

	/**
	 * @param receiver
	 */
	public PDEUpload(final Receiver receiver) {
		super(receiver);
	}

	/**
	 * @param listener
	 * @return Registration
	 */
	public Registration addFileRemoveListener(final ComponentEventListener<FileRemoveEvent> listener) {
		return super.addListener(FileRemoveEvent.class, listener);
    }
}

Now my custom file buffer which allows me to actually clear the files in memory:

package de.fzi.pde.gui.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.vaadin.flow.component.upload.MultiFileReceiver;
import com.vaadin.flow.component.upload.receivers.FileData;

/**
 * @author bender
 *
 * Because the Vaadin one is shite.
 *
 */
public class PDEMultiFileMemoryBuffer implements MultiFileReceiver {

	private static final long serialVersionUID = 1321199489872872281L;
	
	private Map<String, FileData> files = new HashMap<>();

	/**
	 * Constr.
	 */
	public PDEMultiFileMemoryBuffer() {
		super();
	}	

    /**
     * (non-Javadoc)
     * @see com.vaadin.flow.component.upload.Receiver#receiveUpload(java.lang.String, java.lang.String)
     */
    @Override
    public OutputStream receiveUpload(final String fileName, final String MIMEType) {
    	final OutputStream outputBuffer = new ByteArrayOutputStream();
        files.put(fileName, new FileData(fileName, MIMEType, outputBuffer));

        return outputBuffer;
    }

    /**
     * Get the files in memory for this buffer.
     * 
     * @return files in memory
     */
    public Set<String> getFiles() {
        return files.keySet();
    }

    /**
     * Get file data for upload with file name
     * 
     * @param fileName
     *            file name to get upload data for
     * @return file data for filename or null if not found
     */
    public FileData getFileData(final String fileName) {
        return files.get(fileName);
    }

    /**
     * Get the output stream for file.
     * 
     * @param fileName
     *            name of file to get stream for
     * @return file output stream or empty stream if no file found
     */
    public ByteArrayOutputStream getOutputBuffer(final String fileName) {
        if (files.containsKey(fileName)) {
            return (ByteArrayOutputStream) files.get(fileName)
                    .getOutputBuffer();
        }
        
        return new ByteArrayOutputStream();
    }

    /**
     * Get the input stream for file with filename.
     * 
     * @param filename
     *            name of file to get input stream for
     * @return input stream for file or empty stream if file not found
     */
    public InputStream getInputStream(final String filename) {
        if (files.containsKey(filename)) {
            return new ByteArrayInputStream(((ByteArrayOutputStream) files
                    .get(filename).getOutputBuffer()).toByteArray());
        }
        
        return new ByteArrayInputStream(new byte[0]
);
    }
    
    /**
     * @return True if not empty
     */
    public boolean hasFiles() {
    	return !files.isEmpty();
    }
    
    /**
     * @param key
     */
    public void remove(final String key) {
    	files.remove(key);
    }
    
    /**
     * Clear Buffer
     */
    public void clear() {
    	files.clear();
    }
}

And how I tie it together in my actual view:

package de.fzi.pde.framework.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.Notification.Position;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.upload.Upload;

import de.fzi.pde.aml.exception.AMLParseException;
import de.fzi.pde.aml.model.AML;
import de.fzi.pde.ams.exception.AMSException;
import de.fzi.pde.ams.service.IAMS;
import de.fzi.pde.gui.util.PDEMultiFileMemoryBuffer;
import de.fzi.pde.gui.view.PDEPage;
import de.fzi.pde.gui.view.PDEUpload;
import elemental.json.Json;

/**
 * @author bender
 *
 */
public class AMLPage extends PDEPage {

	//...
	
	private final PDEMultiFileMemoryBuffer buffer;
	
	private final Upload upload;
	
	private final Button clearBtn;
	
	//...

	/**
	 * Constr.
	 */
	public AMLPage(final MainView mainView, final IAMS service) {
		//...
		clearBtn = createClearButton();
		buffer = createBuffer();
		upload = createUpload(buffer);		
		//...
	}
	
	/**
	 * (non-Javadoc)
	 * @see de.fzi.pde.gui.view.PDEPage#update()
	 */
	@Override
	public void update() {
		//...
		getClearBtn().setEnabled(getBuffer().hasFiles());
	}

	/**
	 * @return PDEMultiFileMemoryBuffer
	 */
	protected PDEMultiFileMemoryBuffer createBuffer() {
		return new PDEMultiFileMemoryBuffer();
	}
	
	/**
	 * @return Upload
	 */
	protected Upload createUpload(final PDEMultiFileMemoryBuffer buffer) {
		final PDEUpload up = new PDEUpload(buffer);
		up.setAcceptedFileTypes(".aml", ".xml");
		up.addSucceededListener(event -> {
			update();
			Notification.show(String.format("AML-file loaded: %s", event.getFileName()), 5000, Position.BOTTOM_END);
		});
		up.addFileRemoveListener(event -> clear()); // TODO Find out how to identify which file the user removed.
		
		return up;
	}
	
	//...
	
	/**
	 * @return Button
	 */
	protected Button createClearButton() {
		final Button clearBut = new Button("Clear");
		clearBut.addClickListener(e -> clear());
		
		return clearBut;
	}
	
	//...
	
	/**
	 * Clear GUI and memory.
	 */
	protected void clear() {
		getUpload().getElement().setPropertyJson("files", Json.createArray());
		getBuffer().clear();
		update();
	}

	//...

	/**
	 * @return the buffer
	 */
	protected PDEMultiFileMemoryBuffer getBuffer() {
		return buffer;
	}

	/**
	 * @return the upload
	 */
	protected Upload getUpload() {
		return upload;
	}

	//...

	/**
	 * @return the clearBtn
	 */
	protected Button getClearBtn() {
		return clearBtn;
	}
}

\u0000I am trying to solve the problem described in this thread with an alternative approach. \n\nProblem: Uploaded files cannot be removed from Vaadin Upload, since the MultiFileMemoryBuffer does not contain a method to remove a file. Clicking a cross next to a file does not actually remove an uploaded file from the buffer.\n\nIn my implementation, in addition to using the default MultiFileMemoryBuffer, I am using my own collection for storing uploaded files, so I would like to intercept the (now working) \"file-remove\" DOM event to remove this file in my own collection.\n\nAs indicated by Janek Bender in this thread, at the moment it is not possible to correlate the DOM’s \"file-remove\" event to the Vaadin Upload’s SucceededEvent, so there is no way to know which file to remove.\n\nMy thought was to add an extra DOM event listener to catch the initial upload event and get information I could use to correlate with the UUID that is available in the \"file-remove\" event. However, I don’t know what is the name of the DOM event that is fired during upload. What would be the correct DOM event name? Where can I find a full list of these DOM events?\n\nAny thoughts are much appreciated. \uD83D\uDE4F\uD83C\uDFFB\n\nRelated:\n- https://github.com/vaadin/vaadin-upload-flow/issues/63

After much fiddling I’ve cobbled together a MyUpload component which contains a FileRemoveListener that works for me - my FileRemoveEvent contains a fileName which I can use to handle file removal in the custom files list.

I hope this will be useful to somebody else!

 private class MyUpload extends Upload {
        public MyUpload(MultiFileMemoryBuffer buffer) {super(buffer);}

        Registration addFileRemoveListener(ComponentEventListener<FileRemoveEvent> listener) {
            return super.addListener(FileRemoveEvent.class, listener);
        }
    }

    @DomEvent("file-remove")
    public static class FileRemoveEvent extends ComponentEvent<Upload> {
        private String fileName;

        public FileRemoveEvent(Upload source, boolean fromClient, @EventData("event.detail.file.name") JreJsonString fileNameJson) {
            super(source, fromClient);
            fileName = fileNameJson.getString();
        }

        public String getFileName() {
            return fileName;
        }
    }

Rok Pajek:
After much fiddling I’ve cobbled together a MyUpload component which contains a FileRemoveListener that works for me - my FileRemoveEvent contains a fileName which I can use to handle file removal in the custom files list.

I hope this will be useful to somebody else!

 private class MyUpload extends Upload {
        public MyUpload(MultiFileMemoryBuffer buffer) {super(buffer);}

        Registration addFileRemoveListener(ComponentEventListener<FileRemoveEvent> listener) {
            return super.addListener(FileRemoveEvent.class, listener);
        }
    }

    @DomEvent("file-remove")
    public static class FileRemoveEvent extends ComponentEvent<Upload> {
        private String fileName;

        public FileRemoveEvent(Upload source, boolean fromClient, @EventData("event.detail.file.name") JreJsonString fileNameJson) {
            super(source, fromClient);
            fileName = fileNameJson.getString();
        }

        public String getFileName() {
            return fileName;
        }
    }

Thank you but is there a way to identify file on smth else than name ? because I’m allowing multiple files and if users decides to use file1 file2 file1 and then click remove it is matchung based on name and if he has choosen to delete the third one but the first one is found it’s kind of a weird behaviour.