Scan virus for file upload

Hi Jmix team,

We have an antivirus server api. Our customer require scan virus for every uploaded files. We use FileStorageUploadField to upload files .
Where is the best class or interface that we can override the common logic code to scan file?
Please help us!

Hello!

You can scan a file after the uploading it to temporary storage. FileStorageUploadField enables manually handling file using fileStoragePutMode="MANUAL". In this mode you can subscribe to FileUploadSucceededEvent which is fired after writing a file.

See fileStorageUploadField :: Jmix Documentation.

Example from the documentation:

<fileStorageUploadField id="manuallyControlledField"
                        dataContainer="userDc"
                        property="picture"
                        clearButtonVisible="true"
                        fileNameVisible="true"
                        fileStoragePutMode="MANUAL"/>
@Autowired
private TemporaryStorage temporaryStorage;
@ViewComponent
private FileStorageUploadField manuallyControlledField;
@Autowired
private Notifications notifications;

@Subscribe("manuallyControlledField")
public void onManuallyControlledFieldFileUploadSucceeded(
        final FileUploadSucceededEvent<FileStorageUploadField> event) {
    Receiver receiver = event.getReceiver();
    if (receiver instanceof FileTemporaryStorageBuffer) {
        UUID fileId = ((FileTemporaryStorageBuffer) receiver)
                .getFileData().getFileInfo().getId();
        File file = temporaryStorage.getFile(fileId);

        if (file != null) {
            // here you can try to scan
            // ...
        }

        // If it's ok, move it to file storage
        FileRef fileRef = temporaryStorage.putFileIntoStorage(fileId, event.getFileName());
        // Set value to field
        manuallyControlledField.setValue(fileRef);
        // ...
    }
}

Hi Roman,
Thank you for answer, but in jmix application, we have alot UploadStorageFields and include build in upload function in others add-on like Report-addon… We dont want customize manual for each upload field.
We are looking for common solution or common method or function like interceptor http request or override common interface…
Please help us to do this task.

All files are uploaded to TemporaryStorage or immediately to FileStorage. I think these two points where you can try to override logic and scan file.

  1. Extended TemporaryStorage:

    MyTemporaryStorage.class
    @Primary
    @Component("MyTemporaryStorage")
    public class MyTemporaryStorage extends TemporaryStorageImpl {
    
        @Override
        public UUID saveFile(byte[] data) {
            UUID uuid = super.saveFile(data);
    
            File file = tempFiles.get(uuid);
            // Scan file
    
            return uuid;
        }
    
        @Override
        public UUID saveFile(InputStream stream, UploadProgressListener listener) {
            UUID uuid = super.saveFile(stream, listener);
    
            File file = tempFiles.get(uuid);
            // Scan file
    
            return uuid;
        }
    
        @Override
        public FileRef putFileIntoStorage(UUID fileId, String fileName, FileStorage fileStorage) {
            File file = tempFiles.get(fileId);
            // Here we also should scan the file, because file can be created
            // using #createFile().
    
            return super.putFileIntoStorage(fileId, fileName, fileStorage);
        }
    
        @Override
        public FileRef putFileIntoStorage(UUID fileId, String fileName) {
            File file = tempFiles.get(fileId);
            // Here we also should scan the file, because file can be created
            // using #createFile().
    
            return super.putFileIntoStorage(fileId, fileName);
        }
    }
    
  2. You can extend your current FileStorage implementation to scan files and make it default for the application:

    • Define your implementation in Spring configuration. For instance for LocalFileStorage:

      @Bean
      public MyCustomLocalFileStorage secondFileStorage(CoreProperties coreProperties) {
           return new MyCustomLocalFileStorage ("fs2", coreProperties.getWorkDir() + "/fs2");
      }
      
    • Property jmix.core.default-file-storage :: Jmix Documentation.

Hi Roman,
Thank you very much for kindly help.
We will try as suggestion.
By other way, we have used HttpServletRequest interceptor solution is working too.
For some one who have the same issue, here is working code:

public class MyVaadinServlet extends JmixVaadinServlet {
@Autowired
protected FileScanService fileScanService;

public MyVaadinServlet(ApplicationContext applicationContext) {
    super(applicationContext);
}

@Override
protected void service(HttpServletRequest request,
                       HttpServletResponse response)
        throws ServletException, IOException {

        // Check file upload request
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if(isMultipart){
            CachedBodyHttpServletRequest reqNew =
                    new CachedBodyHttpServletRequest(request);
            InputStream reqIs = reqNew.getInputStream();
            FileScanResponse  scanResponse = fileScanService.scanFiles(reqIs);
            if(scanResponse !=null && Boolean.TRUE.equals(scanResponse.getDetected()) ){
                throw new IOException("File upload maybe content the virus. Please upload again");
            }else {
                super.service(reqNew, response);
            }
        }else {
            super.service(request, response);
        }    
}

}

The class "CachedBodyHttpServletRequest " you can refer in https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times