package com.shoppingcart.helpers; import com.shoppingcart.dao.ShopDao; import com.shoppingcart.dao.entity.Application; import com.shoppingcart.dao.entity.Category; import javassist.tools.rmi.ObjectNotFoundException; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; 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.annotation.ImportResource; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; /** * Created by ysalmin on 21.07.2014. * Utils for file processing. */ @Component @ImportResource("classpath:/app-conf.xml") public class StorageUtils { private final Logger logger = LoggerFactory.getLogger(StorageUtils.class); @Autowired private ShopDao shopDao; private @Value("#{props['tmp.path']}") String tmpPath; private @Value("#{props['apps.path']}") String appsPath; private @Value("#{props['date.format']}") String dateFormat; private @Value("#{props['detailed.default.image.path']}") String detailedDefaultImagePath; private @Value("#{props['preview.default.image.path']}") String previewDefaultImagePath; private @Value("#{props['preview.image.width']}") int previewImageWidth; private @Value("#{props['preview.image.height']}") int previewImageHeight; private @Value("#{props['detailed.image.width']}") int detailedImageWidth; private @Value("#{props['detailed.image.height']}") int detailedImageHeight; private @Value("#{props['txt.files.allowed']}") int txtFilesAllowed; private @Value("#{props['total.files.allowed']}") int totalFilesAllowed; private String formattedDate; /** * Initial application archive storing. * * @param file * @param path * @param date * @return true if file stored successfully * @throws IOException */ public boolean storeFile(MultipartFile file, String path, Date date) throws IOException { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); formattedDate = simpleDateFormat.format(date); if (!file.isEmpty()) { byte[] bytes = file.getBytes(); File dir = new File(getFullFileName(path, file)); if (!dir.exists()) { if(!dir.mkdirs()) { throw new IOException("Cant create folders"); } } File serverFile = new File(dir.getAbsolutePath() + File.separator + file.getOriginalFilename()); BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(serverFile)); stream.write(bytes); stream.close(); logger.info("Server File Location=" + serverFile.getAbsolutePath()); return true; } return false; } /** * Extracts archive content to temp dir and process content. * * @param name * @param file * @param path * @return Application based on archive content * @throws ZipException * @throws IOException * @throws ObjectNotFoundException */ public Application extractFileContent(String name, MultipartFile file, String path) throws ZipException, IOException, ObjectNotFoundException { if (!checkFileFormat(file, path)) { logger.error("File format is wrong."); return null; } String source = getFullFileName(path, file) + file.getOriginalFilename(); File destinationFolder = new File(getFullFileName(path, file)); if (!destinationFolder.exists()) { if(!destinationFolder.mkdirs()) { throw new IOException("Cant create folders"); } } ZipFile zipFile = new ZipFile(source); zipFile.extractAll(destinationFolder.getPath()); if (!checkAppContent(destinationFolder.getPath())) { logger.error("App content is wrong."); return null; } return extractAppContent(name, path, destinationFolder.getPath(), file); } /** * Cleaning of files in case of wrong archive content. * * @param file * @param path * @throws IOException */ public void cleanUpFiles(MultipartFile file, String path) throws IOException { File folderToClean = new File(getFullFileName(path, file)); FileUtils.cleanDirectory(folderToClean); if(!folderToClean.delete()){ throw new IOException("Cant remove folder " + file.getName()); } } private boolean checkFileFormat(MultipartFile file, String path) throws ZipException { String source = getFullFileName(path, file) + file.getOriginalFilename(); ZipFile zipFile = new ZipFile(source); if (!zipFile.isValidZipFile()) { logger.error("File has wrong format."); return false; } return true; } private Application extractAppContent(String name, String homePath, String path, MultipartFile multipartFile) throws IOException, ObjectNotFoundException { String folderName = name.replace(' ', '_'); String previewImageUrl = previewDefaultImagePath; String detailedImageUrl = detailedDefaultImagePath; if(!StringUtils.isNotBlank(folderName) || !StringUtils.isNotBlank(previewImageUrl) || !StringUtils.isNotBlank(detailedImageUrl)) { logger.error("Settings for application params are empty."); throw new IllegalArgumentException(); } String categoryName = ""; File appFolder = new File(path); File[] files = appFolder.listFiles(); if (files != null) { for (File file : files) { if (file.getName().toLowerCase().endsWith(".txt")) { Properties properties = new Properties(); InputStream input = new FileInputStream(file); try { properties.load(input); //load properties from file } finally { input.close(); } categoryName = properties.getProperty("package").toLowerCase(); } else if (file.getName().toLowerCase().endsWith(".jpg")) { BufferedImage bufferedImage = ImageIO.read(file); int width = bufferedImage.getWidth(); if (width == previewImageWidth) { previewImageUrl = getImagesFullPath(file, folderName); } else { detailedImageUrl = getImagesFullPath(file, folderName); } } } } else { logger.error("No files in archive."); throw new IOException(); } moveFilesToPermanentLocation(folderName, homePath, multipartFile); Application application = new Application(name, appsPath + folderName + "/" + multipartFile.getOriginalFilename(), previewImageUrl, detailedImageUrl); Category category = shopDao.getCategoryByName(categoryName); application.addCategory(category, categoryName); return application; } private void moveFilesToPermanentLocation(String name, String path, MultipartFile file) throws IOException { File destination = new File(path + "apps" + File.separator + name + File.separator); File source = new File(getFullFileName(path, file)); FileUtils.copyDirectory(source, destination); } private boolean checkAppContent(String path) throws IOException { File appFolder = new File(path); File[] files = appFolder.listFiles(); Short numOfTxtFiles = 0; if (files != null) { if (files.length > totalFilesAllowed) { logger.error("Archive contains more files than allowed in settings."); return false; } for (File file : files) { if (file.isDirectory() || !file.getName().toLowerCase().endsWith(".jpg") && !file.getName().toLowerCase().endsWith(".txt") && !file.getName().toLowerCase().endsWith(".zip")) { logger.error("Archive contains wrong files."); return false; } if (file.getName().toLowerCase().endsWith(".txt")) { numOfTxtFiles++; if (numOfTxtFiles > txtFilesAllowed) { logger.error("Num of txt files is bigger than allowed in settings."); return false; } InputStream input = new FileInputStream(file); Properties properties = new Properties(); try { properties.load(input); } finally { input.close(); } if (properties.size() != 2) { logger.error("App file contains wrong amount of settings."); return false; } if(!properties.containsKey("package") || !properties.containsKey("name")) { logger.error("App file contains wrong settings."); return false; } } if (file.getName().toLowerCase().endsWith(".jpg") && !file.getName().toLowerCase().endsWith(".txt")) { BufferedImage bufferedImage = ImageIO.read(file); int width = bufferedImage.getWidth(); int height = bufferedImage.getHeight(); if (!(width == previewImageWidth && height == previewImageHeight || width == detailedImageWidth && height == detailedImageHeight)) { logger.error("Images in archive have wrong size."); return false; } } } } else { logger.error("Archive don't contain files."); return false; } return true; } private String getImagesFullPath(File file, String folderName) { return appsPath + folderName + "/" + file.getName(); } private String getFullFileName(String path, MultipartFile file) { return path + tmpPath + File.separator + file.getOriginalFilename() + "-" + formattedDate + File.separator; } }