/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo Framework
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.model.internal.file;
import static org.springframework.context.i18n.LocaleContextHolder.getLocale;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.activation.MimetypesFileTypeMap;
import com.google.common.base.Preconditions;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.localization.api.utils.DateUtils;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.file.FileService;
import com.qcadoo.tenant.api.MultiTenantUtil;
@Service
public class FileServiceImpl implements FileService {
@Autowired
private TranslationService translationService;
private static final Logger LOG = LoggerFactory.getLogger(FileServiceImpl.class);
private static FileService instance;
private static final String L_FILE_URL_PREFIX = "/files/";
private File uploadDirectory;
public FileServiceImpl() {
FileServiceImpl.setInstance(this);
}
@Value("${reportPath}")
public void setUploadDirectory(final String uploadDirectory) {
this.uploadDirectory = new File(uploadDirectory);
}
@Override
public String getName(final String path) {
if (!StringUtils.hasText(path)) {
return null;
}
return path.substring(path.lastIndexOf(File.separatorChar) + 1);
}
@Override
public String getLastModificationDate(final String path) {
if (!StringUtils.hasText(path)) {
return null;
}
Date date = new Date(Long.valueOf(path.substring(path.lastIndexOf(File.separatorChar) + 1,
path.lastIndexOf(File.separatorChar) + 14)));
return new SimpleDateFormat(DateUtils.L_DATE_FORMAT, getLocale()).format(date);
}
@Override
public String getUrl(final String path) {
if (!StringUtils.hasText(path)) {
return null;
}
return L_FILE_URL_PREFIX + normalizeSeparators(path.substring(uploadDirectory.getAbsolutePath().length() + 1));
}
private String normalizeSeparators(final String string) {
if ("\\".equals(File.separator)) {
return string.replaceAll("\\\\", "/");
} else {
return string;
}
}
private String denormalizeSeparators(final String string) {
if ("\\".equals(File.separator)) {
return string.replaceAll("/", "\\\\");
} else {
return string;
}
}
@Override
public String getPathFromUrl(final String url) {
String denormalizedUrl = denormalizeSeparators(url);
return uploadDirectory.getAbsolutePath() + File.separator
+ denormalizedUrl.substring(denormalizedUrl.indexOf(File.separatorChar) + L_FILE_URL_PREFIX.length() - 1);
}
@Override
public InputStream getInputStream(final String path) {
if (!StringUtils.hasText(path)) {
return null;
}
try {
return new FileInputStream(new File(path));
} catch (FileNotFoundException e) {
return null;
}
}
@Override
public String upload(final MultipartFile multipartFile) throws IOException {
File file = getFileFromFilenameWithRandomDirectory(multipartFile.getOriginalFilename());
OutputStream output = null;
try {
output = new FileOutputStream(file);
IOUtils.copy(multipartFile.getInputStream(), output);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
IOUtils.closeQuietly(output);
throw e;
}
return file.getAbsolutePath();
}
@Override
public File createExportFile(final String filename) {
return getFileFromFilenameWithRandomDirectory(filename);
}
@Override
public File createReportFile(final String fileName) throws IOException {
return getFileFromFilename(fileName);
}
private File getFileFromFilename(final String filename) throws IOException {
File directory = new File(uploadDirectory, MultiTenantUtil.getCurrentTenantId() + File.separator);
try {
FileUtils.forceMkdir(directory);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
throw e;
}
return new File(directory, getNormalizedFileName(filename.substring(filename.lastIndexOf(File.separator) + 1)));
}
private File getFileFromFilenameWithRandomDirectory(final String filename) {
String date = Long.toString(System.currentTimeMillis());
File directory = new File(uploadDirectory, MultiTenantUtil.getCurrentTenantId() + File.separator
+ date.charAt(date.length() - 1) + File.separator + date.charAt(date.length() - 2) + File.separator);
directory.mkdirs();
return new File(directory, date + "_" + getNormalizedFileName(filename));
}
private String getNormalizedFileName(final String filename) {
return filename.replaceAll("[^a-zA-Z0-9.]+", "_");
}
@Override
public Entity updateReportFileName(final Entity entity, final String dateFieldName, final String name) {
String currentFiles = entity.getStringField("fileName");
if (currentFiles == null) {
currentFiles = "";
}
if (!currentFiles.isEmpty()) {
currentFiles += ",";
}
entity.setField("fileName", currentFiles + getReportFullPath(name, (Date) entity.getField(dateFieldName)));
return entity.getDataDefinition().save(entity);
}
@Override
public File compressToZipFile(List<File> documents, boolean removeCompressed) throws IOException {
Preconditions.checkNotNull(documents, "documents argument is nullable.");
Preconditions.checkArgument(!documents.isEmpty(), "documents list can't be empty");
File zipFile = createExportFile("documents.zip");
FileOutputStream fos = new FileOutputStream(zipFile);
ZipOutputStream zos = new ZipOutputStream(fos);
try {
for (File document : documents) {
ZipEntry ze = new ZipEntry(document.getName());
zos.putNextEntry(ze);
FileInputStream in = new FileInputStream(document);
IOUtils.copy(in, zos);
IOUtils.closeQuietly(in);
zos.closeEntry();
if(removeCompressed) {
remove(document.getAbsolutePath());
}
}
} finally {
IOUtils.closeQuietly(zos);
}
return zipFile;
}
private String getReportFullPath(final String name, final Date date) {
String translatedReportName = translationService.translate(name, LocaleContextHolder.getLocale());
return getReportPath() + translatedReportName + "_"
+ new SimpleDateFormat(DateUtils.L_REPORT_DATE_TIME_FORMAT, getLocale()).format(date);
}
private String getReportPath() {
return uploadDirectory.getAbsolutePath() + File.separator + MultiTenantUtil.getCurrentTenantId() + File.separator;
}
@Override
public String getContentType(final String path) {
return new MimetypesFileTypeMap().getContentType(new File(path));
}
@Override
public void remove(final String path) {
FileUtils.deleteQuietly(new File(path));
}
public static FileService getInstance() {
return instance;
}
private static void setInstance(final FileService instance) {
FileServiceImpl.instance = instance;
}
}