package net.flibusta.persistence.dao.impl;
import net.flibusta.persistence.dao.BookDao;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class BookDaoSql extends JdbcDaoSupport implements BookDao {
private File fileStoragePath;
private Set<String> createdDirs = Collections.synchronizedSet(new HashSet<String>(512));
@Override
public File findBook(String bookId, String type) {
String fileName = findBookPath(bookId, type);
if (fileName == null) {
return null;
}
File file = new File(fileStoragePath, fileName);
if (!file.exists()) {
getJdbcTemplate().update("delete from book where bookid = ? and format = ?", bookId, type);
return null;
}
return file;
}
@Override
public String findBookPath(String bookId, String type) {
List<Map<String, Object>> rows = getJdbcTemplate().queryForList("select file_name from book where bookid = ? and format = ?", bookId, type);
if (rows.size() == 0) {
return null;
}
if (rows.size() > 1) {
getJdbcTemplate().update("delete from book where bookid = ? and format = ?", bookId, type);
throw new RuntimeException("Too many files for " + bookId + " in format " + type);
}
return (String) rows.get(0).get("file_name");
}
@Override
public File addBook(String bookId, String sourceFormat, File sourceFile) {
String storageFileName = makeStorageFileName(bookId, sourceFormat, sourceFile);
boolean hasToCompress = sourceFormat.equals("fb2");
if (hasToCompress) {
storageFileName = storageFileName + ".zip";
}
File storageFile = new File(fileStoragePath, storageFileName);
/*
while (storageFile.exists()) { // avoid name collision
storageFileName = storageFile.getName().substring(0, 1) + storageFile.getName();
storageFile = new File(storageFile.getParentFile(), storageFileName);
}
*/
makeDirs(storageFile);
if (!storageFile.equals(sourceFile)) {
if (!storageFile.exists()) {
if (!hasToCompress) {
try {
FileUtils.moveFile(sourceFile, storageFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
try {
zipFile(sourceFile, storageFile);
sourceFile.delete();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} else {
logger.warn("addBook: file already exists: bookId=" + bookId + " file=" + storageFileName);
sourceFile.delete();
}
}
getJdbcTemplate().update("insert into book (bookid, format, file_name) values (?, ?, ?)", bookId, sourceFormat, storageFileName);
return storageFile;
}
@Override
public void deleteBook(final String bookId) {
getJdbcTemplate().query("select file_name from book where bookid = ?", new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, bookId);
}
},
new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
String file_name = rs.getString("file_name");
FileUtils.deleteQuietly(new File(fileStoragePath, file_name));
}
}
);
getJdbcTemplate().update("delete from book where bookid = ?", bookId);
}
private void makeDirs(File storageFile) {
File parentFile = storageFile.getParentFile();
if (createdDirs.contains(parentFile.getPath())) {
return;
}
if (!parentFile.exists()) {
parentFile.mkdirs();
}
createdDirs.add(parentFile.getPath());
}
private String makeStorageFileName(String bookId, String format, File sourceFile) {
String dirPrefix = new SimpleDateFormat("yyyy/MM/dd/").format(new Date());
String sourceFileName = sourceFile.getName();
String baseName = FilenameUtils.getBaseName(sourceFileName).replaceAll("[^\\p{Alnum}\\.\\_\\-]", "");
return dirPrefix + baseName + "." + format;
// return bookId.substring(0, 2) + "/" + baseName + "." + format;
}
public void setFileStoragePath(String fileStoragePath) {
this.fileStoragePath = new File(fileStoragePath);
}
private File zipFile(File source, File target) throws IOException {
ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(target));
zipOutputStream.setLevel(ZipOutputStream.DEFLATED);
FileInputStream fileInputStream = new FileInputStream(source);
try {
byte[] buffer = new byte[8 * 1024];
ZipEntry entry = new ZipEntry(source.getName());
zipOutputStream.putNextEntry(entry);
int read;
while ((read = fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, read);
}
} catch (Exception e) {
throw new IOException(e);
} finally {
IOUtils.closeQuietly(fileInputStream);
IOUtils.closeQuietly(zipOutputStream);
}
return target;
}
}