Search code examples
springspring-bootspring-thymeleafmultipartfile

One additional duplicate file gets uploaded


I have my FileUpload Controller like this:

@PostMapping("/uploadFile")
public AppUserDocumentUploadResponse uploadFile(@RequestParam("file") MultipartFile file) {
    AppUserDocument dbFile = appUserDocumentStorageService.storeFile(file);

    String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
            .path("/downloadFile/")
            .path(dbFile.getId().toString())
            .toUriString();

    return new AppUserDocumentUploadResponse(dbFile.getDbfileName(), fileDownloadUri,
            file.getContentType(), file.getSize());
}

@PostMapping("/uploadMultipleFiles")
public List<AppUserDocumentUploadResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
    return Arrays.asList(files)
            .stream()
            //.peek(fileBeingProcessed -> log.info("Processing File (PEEK2): {} ", fileBeingProcessed))
            .map(file -> uploadFile(file))
            .collect(Collectors.toList());
}

My Service is:

public AppUserDocument storeFile(MultipartFile file) {
    
    String fileName = StringUtils.cleanPath(file.getOriginalFilename());
    try {
        if (fileName.contains("..")) {
            throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
        }
        AppUserDocument dbFile = new AppUserDocument(UUID.randomUUID().toString(), fileName, file.getContentType(),
                file.getSize(), file.getBytes());

        return dbFileRepository.save(dbFile);
    } catch (IOException ex) {
        throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
    }
}

Model goes like this:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "AppDBFiles")
@Table(name = "app_db_files")
public class AppUserDocument extends Auditable<String>  {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "dbfileuuid", unique = true)
    @GenericGenerator(name = "uuid", strategy = "uuid4")
    private String dbfileuuid;

    @Column(name = "dbfilename")
    private String dbfileName;

    @Column(name = "dbfiletype")
    private String dbfileType;
    
    @Column(name = "dbfilesize")
    private long dbfileSize;

    @Lob
    @Column(name = "dbfiledata")
    private byte[] dbfileData;
    
    @Version
    @Column(name="optlock")
    private Integer version;


    public AppUserDocument(String dbfileName, String dbfileType, long dbfileSize, byte[] dbfileData) {
        super();
        this.dbfileName = dbfileName;
        this.dbfileType = dbfileType;
        this.dbfileSize = dbfileSize;
        this.dbfileData = dbfileData;
    }
    
    public AppUserDocument(String dbfileuuid, String dbfileName, String dbfileType, long dbfileSize, byte[] dbfileData) {
        super();
        this.dbfileName = dbfileName;
        this.dbfileType = dbfileType;
        this.dbfileSize = dbfileSize;
        this.dbfileData = dbfileData;
    }

}

And my (vanilla) JS goes like this:

function uploadMultipleFiles(files) {
    var formData = new FormData();
    for (var index = 0; index < files.length; index++) {
        formData.append("files", files[index]);
    }
    const fileField = document.querySelector('input[type="file"]');

    //formData.append('filecusomName', 'abc123');
    formData.append('files', fileField.files[0]);

    fetch('/uploadMultipleFiles', {
        method: 'POST',
        body: formData
    })
        .then((response) => response.json())
        .then((response) => {
            console.log('Success:', response);
            multipleFileUploadError.style.display = "none";
            var content = "<p>All Files Uploaded Successfully</p>";
            document.getElementById("multipleFileUploadInput").value = null;
            for (var i = 0; i < response.length; i++) {
                content += "<p>DownloadUrl : <a href='" + response[i].fileDownloadUri + "' target='_blank'>" + response[i].fileDownloadUri + "</a></p>";
            }
            multipleFileUploadSuccess.innerHTML = content;
            multipleFileUploadSuccess.style.display = "block";
        })
        .catch((error) => {
            multipleFileUploadSuccess.style.display = "none";
            multipleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
            console.error('Error:', error);
        });
}

All goes well except one additional file gets uploaded which is duplicate of the first file.

This is what my console logs show:

Success: 
(3) [{…}, {…}, {…}]
0
: 
fileDownloadUri
: 
"http://localhost:8080/downloadFile/39"
fileName
: 
"sample.pdf"
fileType
: 
"application/pdf"
size
: 
357896
[[Prototype]]
: 
Object
1
: 
fileDownloadUri
: 
"http://localhost:8080/downloadFile/40"
fileName
: 
"sample2.pdf"
fileType
: 
"application/pdf"
size
: 
357896
[[Prototype]]
: 
Object
2
: 
fileDownloadUri
: 
"http://localhost:8080/downloadFile/41"
fileName
: 
"sample.pdf"
fileType
: 
"application/pdf"
size
: 
357896
[[Prototype]]
: 
Object
length
: 
3

And my DB looks like this: enter image description here

Uncommenting the .peek(fileBein in my controller also shows this (twice for the duplicate file):

2022-12-07T00:23:25.971-05:00  INFO 11344 --- [nio-8080-exec-2] c.c.s.c.AppUserDocumentRestController    : Processing File 

What I am doing wrong here? Also not sure where to debug this situation. Any pointers/directions will be greatly appreciated.


Solution

  • Please once go through your JS file:

    function uploadMultipleFiles(files) {
        var formData = new FormData();
        for (var index = 0; index < files.length; index++) {
            formData.append("files", files[index]);
        }
        const fileField = document.querySelector('input[type="file"]');
    
        //formData.append('filecusomName', 'abc123');
        formData.append('files', fileField.files[0]);
    
        fetch('/uploadMultipleFiles', {
            method: 'POST',
            body: formData
        })
    ...
    ...
    }
    

    The 9th line formData.append('files', fileField.files[0]); is adding duplicate of First file in FormData.