FileLoader.openStream() triggers FileNotFoundException

Hi,

I am getting the exception in the topic of this post, when executing the following code:

public void onShowUploadDialogButtonClick() {

        FileUploadDialog dialog = (FileUploadDialog) screens.create("fileUploadDialog", OpenMode.DIALOG);

        dialog.addCloseWithCommitListener(() -> {
            UUID fileId = dialog.getFileId();
            String fileName = dialog.getFileName();

            File file = fileUploadingAPI.getFile(fileId);
            FileDescriptor fileDescriptor = fileUploadingAPI.getFileDescriptor(fileId, fileName);
            try {
                fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor);
                InputStream is = fileLoader.openStream(fileDescriptor);
                byte[] bb = IOUtils.toByteArray(is);
                ArchivoAdjunto aa = new ArchivoAdjunto();
                aa.setColeccionArchivosAdjuntos(ubicacionDc.getItem().getColeccionArchivosAdjuntos());
                aa.setDescripcion(fileDescriptor.getName());
                aa.setExtension(fileDescriptor.getExtension());
                aa.setMimeType("");
                aa.setNombreArchivo(fileDescriptor.getName());
                aa.setNombreArchivoOriginal(fileDescriptor.getName());
                aa.setRepresentacionSerial(bb);
                aa.setTamano(bb.length);
                aa.getColeccionArchivosAdjuntos().getArchivos().add(aa);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        screens.show(dialog);

}

Have tried some variants to this code and always get the same result.

If uploading files through the External Files in the Administration menu, it works fine, so doesn’t seem to be any config setting. Somehow the code snippets extracted from the internet don’t work in my case.

I am running CUBA on Ubuntu with CUBA Studio.

Many thanks for any insights.

Regards,

Carlos Conti.

Hi,
I have the same problem. I found reason in LocalFileExchangeServiceBean in downloadFile method. This function try to load not existed FileDescriptor (new)

@Override
public InputStream downloadFile(FileDescriptor fileDescriptor) throws FileStorageException {
    FileDescriptor descriptor;
    try {
        // FileDescriptor must be available for the current user and be non deleted
        descriptor = dataManager.secure().reload(fileDescriptor, View.LOCAL);
    } catch (EntityAccessException e) {
        throw new FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, fileDescriptor.getName(), e);
    }

    return fileStorage.openStream(descriptor);
}

I created extended version of this bean (in Kotlin)

class ExtLocalFileExchangeServiceBean : LocalFileExchangeServiceBean() {@Inject
private lateinit var entityStates: EntityStates

@Throws(FileStorageException::class)
override fun downloadFile(fileDescriptor: FileDescriptor): InputStream {
    val descriptor: FileDescriptor
    descriptor = try {
        // FileDescriptor must be available for the current user and be non deleted
        if (!entityStates.isNew(fileDescriptor)) {
            dataManager.secure().reload(fileDescriptor, View.LOCAL)
        } else {
            fileDescriptor
        }
    } catch (e: EntityAccessException) {
        throw FileStorageException(FileStorageException.Type.FILE_NOT_FOUND, fileDescriptor.name, e)
    }

    return fileStorage.openStream(descriptor)
}}

Regards
Marcin

Hi, Carlos!

The FileNotFoundException appears because the FileDescriptor entity has not been saved in the data store. Try adding dataManager.commit(fileDescriptor) after the fileUploadingAPI.putFileIntoStorage(fileId, fileDescriptor) line.
You can find an example of using the FileUploadDialog in the documentation.

Regards,
Gleb

1 Like

Many thanks Gleb,

it works. However to understand a bit better what’s going on… if I call

dataManager.commit(fileDescriptor)

what do I exactly do behind the scene? execute an isolated transaction only for the file?

after trying it succeeded, also because the main transaction (with the host standardeditor) wasn’t affected. Don’t quite understand yet the scope of some actions of the dataManager. If you can shed some light it would be great.

Mark as solved. Many thanks for your valuable help.

Regards,

Carlos.

Hi @carloscz25,

commit(CommitContext) – saves a set of entities passed in CommitContext to the database. The method returns the set of entity instances returned by EntityManager.merge(); essentially these are fresh instances just updated in DB.

In this case, calling DataManager.commit() should save the FileDescriptor instance in the database.The saved instance returned by this method can be set to an attribute of an entity related to this file. Here, FileDescriptor is simply stored in the database. The file will be available through the Administration > External Files screen.

You can read more about the file upload process using the FileUploadField in the documentation.

Regards,
Gleb

Hello,

solved. Thanks a lot. However I don’t understand why I needo to commit changes. IN this sense commit means writing the filedescriptor in the file table, so I presume the stream is opened provided the information from that table.

Additionally I presume the datamanager.commit(), indeed commits a totally different transaction than the one managing the host standardeditor use case… otherwhise It would trigger errors in the host transaction.

Is that correct?

Thanks.
Carlos.

Hi,

This was done as a security fix. Allowing to download any non-persistent file with arbitrarily filled attributes is a potential security hole.

Yes, DataManager always starts its own transaction. This doc page might be useful for you:
https://doc.cuba-platform.com/manual-7.1/dm_vs_em.html