#182: thumbnail generation asynchronous

This commit is contained in:
Benjamin Gamard 2018-03-14 15:13:09 +01:00
parent 1e57ee5fb3
commit 2a619849f4
4 changed files with 56 additions and 59 deletions

View File

@ -26,13 +26,6 @@ public class FileCreatedAsyncEvent extends UserEvent {
*/ */
private Path unencryptedFile; private Path unencryptedFile;
/**
* Unencrypted file containing PDF representation
* of the original file. May be null if the PDF conversion is not
* necessary or not possible.
*/
private Path unencryptedPdfFile;
public File getFile() { public File getFile() {
return file; return file;
} }
@ -58,15 +51,6 @@ public class FileCreatedAsyncEvent extends UserEvent {
return this; return this;
} }
public Path getUnencryptedPdfFile() {
return unencryptedPdfFile;
}
public FileCreatedAsyncEvent setUnencryptedPdfFile(Path unencryptedPdfFile) {
this.unencryptedPdfFile = unencryptedPdfFile;
return this;
}
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)

View File

@ -2,15 +2,23 @@ package com.sismics.docs.core.listener.async;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import com.sismics.docs.core.dao.jpa.FileDao; import com.sismics.docs.core.dao.jpa.FileDao;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.dao.lucene.LuceneDao; import com.sismics.docs.core.dao.lucene.LuceneDao;
import com.sismics.docs.core.event.FileCreatedAsyncEvent; import com.sismics.docs.core.event.FileCreatedAsyncEvent;
import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.FileUtil; import com.sismics.docs.core.util.FileUtil;
import com.sismics.docs.core.util.PdfUtil;
import com.sismics.docs.core.util.TransactionUtil; import com.sismics.docs.core.util.TransactionUtil;
import com.sismics.util.mime.MimeTypeUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import java.nio.file.Path;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* Listener on file created. * Listener on file created.
@ -26,22 +34,55 @@ public class FileCreatedAsyncListener {
/** /**
* File created. * File created.
* *
* @param fileCreatedAsyncEvent File created event * @param event File created event
*/ */
@Subscribe @Subscribe
public void on(final FileCreatedAsyncEvent fileCreatedAsyncEvent) { public void on(final FileCreatedAsyncEvent event) {
if (log.isInfoEnabled()) { if (log.isInfoEnabled()) {
log.info("File created event: " + fileCreatedAsyncEvent.toString()); log.info("File created event: " + event.toString());
}
// Guess the mime type a second time, for open document format (first detected as simple ZIP file)
final File file = event.getFile();
file.setMimeType(MimeTypeUtil.guessOpenDocumentFormat(file, event.getUnencryptedFile()));
// Convert to PDF if necessary (for thumbnail and text extraction)
Path unencryptedPdfFile = null;
try {
unencryptedPdfFile = PdfUtil.convertToPdf(file, event.getUnencryptedFile());
} catch (Exception e) {
log.error("Unable to convert to PDF", e);
}
// Get the user from the database
final AtomicReference<User> user = new AtomicReference<>();
TransactionUtil.handle(new Runnable() {
@Override
public void run() {
UserDao userDao = new UserDao();
user.set(userDao.getById(event.getUserId()));
}
});
if (user.get() == null) {
// The user has been deleted meanwhile
return;
}
// Generate file variations
try {
Cipher cipher = EncryptionUtil.getEncryptionCipher(user.get().getPrivateKey());
FileUtil.saveVariations(file, event.getUnencryptedFile(), unencryptedPdfFile, cipher);
} catch (Exception e) {
log.error("Unable to generate thumbnails", e);
} }
// Extract text content from the file // Extract text content from the file
final File file = fileCreatedAsyncEvent.getFile();
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getLanguage(), file, final String content = FileUtil.extractContent(event.getLanguage(), file,
fileCreatedAsyncEvent.getUnencryptedFile(), fileCreatedAsyncEvent.getUnencryptedPdfFile()); event.getUnencryptedFile(), unencryptedPdfFile);
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime)); log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
// Store the text content in the database // Save the file to database
TransactionUtil.handle(new Runnable() { TransactionUtil.handle(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -58,7 +99,7 @@ public class FileCreatedAsyncListener {
// Update Lucene index // Update Lucene index
LuceneDao luceneDao = new LuceneDao(); LuceneDao luceneDao = new LuceneDao();
luceneDao.createFile(fileCreatedAsyncEvent.getFile()); luceneDao.createFile(event.getFile());
FileUtil.endProcessingFile(file.getId()); FileUtil.endProcessingFile(file.getId());
} }

View File

@ -121,29 +121,6 @@ public class FileUtil {
return ocrFile(image, language); return ocrFile(image, language);
} }
/**
* Save a file on the storage filesystem.
*
* @param unencryptedFile Unencrypted file
* @param unencryptedPdfFile Unencrypted PDF file
* @param file File to save
* @param privateKey Private key used for encryption
*/
public static void save(Path unencryptedFile, Path unencryptedPdfFile, File file, String privateKey) throws Exception {
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
Files.copy(new CipherInputStream(inputStream, cipher), path);
}
// Generate file variations (errors non-blocking)
try {
saveVariations(file, unencryptedFile, unencryptedPdfFile, cipher);
} catch (Exception e) {
log.error("Unable to generate thumbnails", e);
}
}
/** /**
* Generate file variations. * Generate file variations.
* *
@ -152,7 +129,7 @@ public class FileUtil {
* @param unencryptedPdfFile Unencrypted PDF file * @param unencryptedPdfFile Unencrypted PDF file
* @param cipher Cipher to use for encryption * @param cipher Cipher to use for encryption
*/ */
private static void saveVariations(File file, Path unencryptedFile, Path unencryptedPdfFile, Cipher cipher) throws Exception { public static void saveVariations(File file, Path unencryptedFile, Path unencryptedPdfFile, Cipher cipher) throws Exception {
BufferedImage image = null; BufferedImage image = null;
if (ImageUtil.isImage(file.getMimeType())) { if (ImageUtil.isImage(file.getMimeType())) {
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) { try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
@ -262,14 +239,12 @@ public class FileUtil {
file.setUserId(userId); file.setUserId(userId);
String fileId = fileDao.create(file, userId); String fileId = fileDao.create(file, userId);
// Guess the mime type a second time, for open document format (first detected as simple ZIP file)
file.setMimeType(MimeTypeUtil.guessOpenDocumentFormat(file, unencryptedFile));
// Convert to PDF if necessary (for thumbnail and text extraction)
java.nio.file.Path unencryptedPdfFile = PdfUtil.convertToPdf(file, unencryptedFile);
// Save the file // Save the file
FileUtil.save(unencryptedFile, unencryptedPdfFile, file, user.getPrivateKey()); Cipher cipher = EncryptionUtil.getEncryptionCipher(user.getPrivateKey());
Path path = DirectoryUtil.getStorageDirectory().resolve(file.getId());
try (InputStream inputStream = Files.newInputStream(unencryptedFile)) {
Files.copy(new CipherInputStream(inputStream, cipher), path);
}
// Update the user quota // Update the user quota
user.setStorageCurrent(user.getStorageCurrent() + fileSize); user.setStorageCurrent(user.getStorageCurrent() + fileSize);
@ -283,7 +258,6 @@ public class FileUtil {
fileCreatedAsyncEvent.setLanguage(language); fileCreatedAsyncEvent.setLanguage(language);
fileCreatedAsyncEvent.setFile(file); fileCreatedAsyncEvent.setFile(file);
fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile); fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile);
fileCreatedAsyncEvent.setUnencryptedPdfFile(unencryptedPdfFile);
ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent); ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent);
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent(); DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();

View File

@ -17,7 +17,6 @@ import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.DirectoryUtil; import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.docs.core.util.EncryptionUtil; import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.FileUtil; import com.sismics.docs.core.util.FileUtil;
import com.sismics.docs.core.util.PdfUtil;
import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.exception.ServerException; import com.sismics.rest.exception.ServerException;
@ -202,7 +201,6 @@ public class FileResource extends BaseResource {
fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage()); fileCreatedAsyncEvent.setLanguage(documentDto.getLanguage());
fileCreatedAsyncEvent.setFile(file); fileCreatedAsyncEvent.setFile(file);
fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile); fileCreatedAsyncEvent.setUnencryptedFile(unencryptedFile);
fileCreatedAsyncEvent.setUnencryptedPdfFile(PdfUtil.convertToPdf(file, unencryptedFile));
ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent); ThreadLocalContext.get().addAsyncEvent(fileCreatedAsyncEvent);
DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent(); DocumentUpdatedAsyncEvent documentUpdatedAsyncEvent = new DocumentUpdatedAsyncEvent();