parent
							
								
									566b408ddd
								
							
						
					
					
						commit
						678c140a94
					
				@ -0,0 +1,103 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Abstraktni třída pro práci se soubory.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Pepa Rokos
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractFileStorage<T> implements FileStorage<T> {
 | 
			
		||||
 | 
			
		||||
	protected void saveFileDataToPath(byte[] data, String path) {
 | 
			
		||||
 | 
			
		||||
		File file = new File(path);
 | 
			
		||||
		FileOutputStream os = null;
 | 
			
		||||
		try {
 | 
			
		||||
			os = new FileOutputStream(file);
 | 
			
		||||
			os.write(data);
 | 
			
		||||
			os.flush();
 | 
			
		||||
			os.close();
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		} finally {
 | 
			
		||||
			try {
 | 
			
		||||
				if (os != null) {
 | 
			
		||||
					os.flush();
 | 
			
		||||
					os.close();
 | 
			
		||||
				}
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
				throw new StorageException("Cannot close stream", e.getCause());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected void saveFileToPtah(File file, String path) {
 | 
			
		||||
		File dest = new File(path + File.pathSeparator + file.getName());
 | 
			
		||||
		FileOutputStream fos = null;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			fos = new FileOutputStream(dest);
 | 
			
		||||
			fos.write(readFile(file));
 | 
			
		||||
			fos.flush();
 | 
			
		||||
			fos.close();
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			throw new StorageException("Cannot move file: " + file.getName(), e.getCause());
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new StorageException("Cannot move file: " + file.getName(), e.getCause());
 | 
			
		||||
		} finally {
 | 
			
		||||
			if (fos != null) {
 | 
			
		||||
				try {
 | 
			
		||||
					fos.flush();
 | 
			
		||||
					fos.close();
 | 
			
		||||
				} catch (IOException e) {
 | 
			
		||||
					throw new StorageException("Cannot close stream", e.getCause());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected void removeFileByPath(String path) {
 | 
			
		||||
		File f = new File(path);
 | 
			
		||||
		if (!f.delete()) {
 | 
			
		||||
			throw new StorageException("Cannot delete file: " + path);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected byte[] fileDataFromPath(String path) {
 | 
			
		||||
		File f = new File(path);
 | 
			
		||||
		return readFile(f);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected byte[] readFile(File file) {
 | 
			
		||||
		byte[] out = new byte[(int) file.length()];
 | 
			
		||||
 | 
			
		||||
		FileInputStream fis = null;
 | 
			
		||||
		try {
 | 
			
		||||
			fis = new FileInputStream(file);
 | 
			
		||||
			fis.read(out);
 | 
			
		||||
			fis.close();
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			throw new StorageException("File cannot be found: " + file.getName(), e.getCause());
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new StorageException("Cannot read file: " + file.getName(), e.getCause());
 | 
			
		||||
		} finally {
 | 
			
		||||
			if (fis != null) {
 | 
			
		||||
				try {
 | 
			
		||||
					fis.close();
 | 
			
		||||
				} catch (IOException e) {
 | 
			
		||||
					throw new StorageException("Cannot close stream", e.getCause());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protected File fileFromPath(String path) {
 | 
			
		||||
		return new File(path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import info.bukova.isspst.data.FileMetainfo;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Rozhraní servisního objektu pro práci s přílohami u záznamů. Přílohy jsou reprezentovány objektem FileMetainfo,
 | 
			
		||||
 * kde je zobrazované jméno souboru, vygenerovaný GUID, pod kterým je soubor fyzicky uložen a MD5 součet používaný
 | 
			
		||||
 * pro deduplikaci.
 | 
			
		||||
 *
 | 
			
		||||
 * @see info.bukova.isspst.data.FileMetainfo
 | 
			
		||||
 *
 | 
			
		||||
 * @author  Pepa Rokos
 | 
			
		||||
 */
 | 
			
		||||
public interface DocumentFileStorage extends FileStorage<FileMetainfo> {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Uloží data předaná jako byte[] do uložiště příloh a vrátí metainformace.
 | 
			
		||||
	 * Metoda řeší deduplikaci souborů, takže v uložišti je fyzicky vždy jen jedna kopie.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @see info.bukova.isspst.data.FileMetainfo
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param data Data souboru
 | 
			
		||||
	 * @param name Zobrazovaný název souboru
 | 
			
		||||
	 * @return Metainformace o souboru
 | 
			
		||||
	 */
 | 
			
		||||
	public FileMetainfo saveAndCreateInfo(byte[] data, String name);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Uloží data předaná jako File do uložiště příloh a vrátí metainformace.
 | 
			
		||||
	 * Metoda řeší deduplikaci souborů, takže v uložišti je fyzicky vždy jen jedna kopie.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @see info.bukova.isspst.data.FileMetainfo
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param file Objekt reprezentující soubor
 | 
			
		||||
	 * @param name Zobrazovaný název souboru
 | 
			
		||||
	 * @return Metainformace o souboru
 | 
			
		||||
	 */
 | 
			
		||||
	public FileMetainfo saveAndCreateInfo(File file, String name);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Vrátí metainformace podle fyzické cesty v uložišti.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param path Cesta k souboru v uložišti
 | 
			
		||||
	 * @return Metainformace o souboru
 | 
			
		||||
	 */
 | 
			
		||||
	public FileMetainfo getMetainfoForPath(String path);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Bezpečně odstraní přílohu ze záznamu. Záznam o příloze odstraní z kolekce příloh a následně zavolá metodu
 | 
			
		||||
	 * removeFile(FileMetainfo info), která soubor fyzicky smaže, pokud už není nikam linkovaný.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param entity Entita s přílohami
 | 
			
		||||
	 * @param metaInfo Příloha k odstranění
 | 
			
		||||
	 */
 | 
			
		||||
	public void removeAttachment(EntityWithAttachment entity, FileMetainfo metaInfo);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Bezpečně odstraní všechny přílohy ze záznamu.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param entity Entita s přílohami
 | 
			
		||||
	 */
 | 
			
		||||
	public void removaAllAttachments(EntityWithAttachment entity);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,222 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import info.bukova.isspst.dao.QueryDao;
 | 
			
		||||
import info.bukova.isspst.data.FileMetainfo;
 | 
			
		||||
import org.apache.commons.codec.binary.Hex;
 | 
			
		||||
import org.hibernate.Query;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Pepa Rokos
 | 
			
		||||
 *
 | 
			
		||||
 * Třída pro práci s uložištěm příloh u záznamů. Na soubory příloh se odkazuje pomocí objektů FileMetainfo.
 | 
			
		||||
 *
 | 
			
		||||
 * @see info.bukova.isspst.data.FileMetainfo
 | 
			
		||||
 */
 | 
			
		||||
public class DocumentFileStorageImpl extends AbstractFileStorage<FileMetainfo> implements DocumentFileStorage {
 | 
			
		||||
 | 
			
		||||
	private String rootPath;
 | 
			
		||||
	@Autowired
 | 
			
		||||
	private QueryDao queryDao;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Nastavuje kořenová adresář pro ukládání příloh. Nastavuje se absolutní cesta na filesystému serveru.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param rootPath kořenový adresář pro přílohy
 | 
			
		||||
	 */
 | 
			
		||||
	public void setRootPath(String rootPath) {
 | 
			
		||||
		this.rootPath = rootPath;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void saveFile(byte[] data, FileMetainfo fileID) {
 | 
			
		||||
		String fileName = generateFileName(fileID.getFileName());
 | 
			
		||||
 | 
			
		||||
		saveFileDataToPath(data, rootPath + File.separator + fileName);
 | 
			
		||||
		fileID.setPathInFilesystem(fileName);
 | 
			
		||||
		fileID.setContentType(MimeTypes.getContentType(data, fileName));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void saveFile(File file, FileMetainfo fileID) {
 | 
			
		||||
		String fileName = generateFileName(fileID.getFileName());
 | 
			
		||||
 | 
			
		||||
		saveFileToPtah(file, rootPath + File.separator + fileName);
 | 
			
		||||
		fileID.setPathInFilesystem(fileName);
 | 
			
		||||
		fileID.setContentType(MimeTypes.getContentType(readFile(file), fileName));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String generateFileName(String originalName) {
 | 
			
		||||
		String extension = null;
 | 
			
		||||
		String fileName = UUID.randomUUID().toString();
 | 
			
		||||
 | 
			
		||||
		if (originalName != null && !originalName.isEmpty()) {
 | 
			
		||||
			extension = MimeTypes.fileExtension(originalName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (extension != null) {
 | 
			
		||||
			fileName = fileName + "." + extension;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return fileName;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public void removeFile(FileMetainfo fileID) {
 | 
			
		||||
		if (fileID.getPathInFilesystem() == null || fileID.getPathInFilesystem().isEmpty()) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (infosForPath(fileID.getPathInFilesystem()).size() <= 1) {
 | 
			
		||||
			removeFileByPath(rootPath + File.separator + fileID.getPathInFilesystem());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void moveFile(String source, String destination) {
 | 
			
		||||
		throw new UnsupportedOperationException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void createDirectory(String dir) {
 | 
			
		||||
		throw new UnsupportedOperationException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public byte[] fileData(FileMetainfo fileID) {
 | 
			
		||||
		if (fileID.getPathInFilesystem() != null && !fileID.getPathInFilesystem().isEmpty()) {
 | 
			
		||||
			return fileDataFromPath(rootPath + File.separator + fileID.getPathInFilesystem());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public File file(FileMetainfo fileID) {
 | 
			
		||||
		if (fileID.getPathInFilesystem() != null && !fileID.getPathInFilesystem().isEmpty()) {
 | 
			
		||||
			return fileFromPath(rootPath + File.separator + fileID.getPathInFilesystem());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean dirExists(String path) {
 | 
			
		||||
		throw new UnsupportedOperationException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String serverPath(FileMetainfo fileID) {
 | 
			
		||||
		return "/api/dl/" + fileID.getPathInFilesystem() + "/" + fileID.getFileName();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public FileMetainfo saveAndCreateInfo(byte[] data, String name) {
 | 
			
		||||
		FileMetainfo metaInfo = new FileMetainfo();
 | 
			
		||||
		metaInfo.setFileName(name);
 | 
			
		||||
 | 
			
		||||
		if (!checkForDuplicate(new ByteArrayInputStream(data), metaInfo)) {
 | 
			
		||||
			saveFile(data, metaInfo);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return metaInfo;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public FileMetainfo saveAndCreateInfo(File file, String name) {
 | 
			
		||||
		FileMetainfo metaInfo = new FileMetainfo();
 | 
			
		||||
		metaInfo.setFileName(name);
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			if (!checkForDuplicate(new FileInputStream(file), metaInfo)) {
 | 
			
		||||
				saveFile(file, metaInfo);
 | 
			
		||||
			}
 | 
			
		||||
		} catch (FileNotFoundException e) {
 | 
			
		||||
			//TODO: ošetřit
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return metaInfo;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public FileMetainfo getMetainfoForPath(String path) {
 | 
			
		||||
		if (infosForPath(path).isEmpty()) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return infosForPath(path).get(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private List<FileMetainfo> infosForPath(String path) {
 | 
			
		||||
		Query q = queryDao.getQuery("from FileMetainfo info where info.pathInFilesystem = :path");
 | 
			
		||||
		q.setString("path", path);
 | 
			
		||||
 | 
			
		||||
		return q.list();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean checkForDuplicate(InputStream is, FileMetainfo info) {
 | 
			
		||||
		String md5 = null;
 | 
			
		||||
		try {
 | 
			
		||||
			MessageDigest md = MessageDigest.getInstance("MD5");
 | 
			
		||||
			byte[] dataBytes = new byte[1024];
 | 
			
		||||
			int nread = 0;
 | 
			
		||||
 | 
			
		||||
			while ((nread = is.read(dataBytes)) != -1) {
 | 
			
		||||
				md.update(dataBytes, 0, nread);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			md5 = new String(Hex.encodeHex(md.digest()));
 | 
			
		||||
		} catch (NoSuchAlgorithmException e) {
 | 
			
		||||
			//TODO: ošetřit
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
			return false;
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Query q = queryDao.getQuery("select info from FileMetainfo info where info.md5 = :md5");
 | 
			
		||||
		q.setString("md5", md5);
 | 
			
		||||
		List<FileMetainfo> found = (List<FileMetainfo>) q.list();
 | 
			
		||||
 | 
			
		||||
		if (!found.isEmpty()) {
 | 
			
		||||
			FileMetainfo foundInfo = found.get(0);
 | 
			
		||||
			info.setPathInFilesystem(foundInfo.getPathInFilesystem());
 | 
			
		||||
			info.setMd5(foundInfo.getMd5());
 | 
			
		||||
			info.setContentType(foundInfo.getContentType());
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		} else {
 | 
			
		||||
			info.setMd5(md5);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public void removeAttachment(EntityWithAttachment entity, FileMetainfo metaInfo) {
 | 
			
		||||
		entity.removeAttachment(metaInfo);
 | 
			
		||||
		removeFile(metaInfo);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	@Transactional
 | 
			
		||||
	public void removaAllAttachments(EntityWithAttachment entity) {
 | 
			
		||||
		for (FileMetainfo metaInfo : entity.getAttachedFiles()) {
 | 
			
		||||
			removeFile(metaInfo);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		entity.getAttachedFiles().clear();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import info.bukova.isspst.data.FileMetainfo;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Rozhraní datových entit s přílohami.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Pepa Rokos
 | 
			
		||||
 */
 | 
			
		||||
public interface EntityWithAttachment {
 | 
			
		||||
 | 
			
		||||
	public List<FileMetainfo> getAttachedFiles();
 | 
			
		||||
	public void addAttachment(FileMetainfo metaInfo);
 | 
			
		||||
	public void removeAttachment(FileMetainfo metainfo);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,284 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.StringTokenizer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by pepa on 30.1.15.
 | 
			
		||||
 */
 | 
			
		||||
public class MimeTypes {
 | 
			
		||||
 | 
			
		||||
	public static String getContentType(byte[] data)
 | 
			
		||||
	{
 | 
			
		||||
		return getContentType(data, null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static String getContentType(byte[] data, String name)
 | 
			
		||||
	{
 | 
			
		||||
		if (data == null)
 | 
			
		||||
		{
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		byte[] header = new byte[11];
 | 
			
		||||
		System.arraycopy(data, 0, header, 0, Math.min(data.length, header.length));
 | 
			
		||||
		int c1 = header[0] & 0xff;
 | 
			
		||||
		int c2 = header[1] & 0xff;
 | 
			
		||||
		int c3 = header[2] & 0xff;
 | 
			
		||||
		int c4 = header[3] & 0xff;
 | 
			
		||||
		int c5 = header[4] & 0xff;
 | 
			
		||||
		int c6 = header[5] & 0xff;
 | 
			
		||||
		int c7 = header[6] & 0xff;
 | 
			
		||||
		int c8 = header[7] & 0xff;
 | 
			
		||||
		int c9 = header[8] & 0xff;
 | 
			
		||||
		int c10 = header[9] & 0xff;
 | 
			
		||||
		int c11 = header[10] & 0xff;
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE)
 | 
			
		||||
		{
 | 
			
		||||
			return "application/java-vm";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0 && c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1)
 | 
			
		||||
		{
 | 
			
		||||
			// if the name is set then check if it can be validated by name, because it could be a xls or powerpoint
 | 
			
		||||
			String contentType = guessContentTypeFromName(name);
 | 
			
		||||
			if (contentType != null)
 | 
			
		||||
			{
 | 
			
		||||
				return contentType;
 | 
			
		||||
			}
 | 
			
		||||
			return "application/msword";
 | 
			
		||||
		}
 | 
			
		||||
		if (c1 == 0x25 && c2 == 0x50 && c3 == 0x44 && c4 == 0x46 && c5 == 0x2d && c6 == 0x31 && c7 == 0x2e)
 | 
			
		||||
		{
 | 
			
		||||
			return "application/pdf";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x38 && c2 == 0x42 && c3 == 0x50 && c4 == 0x53 && c5 == 0x00 && c6 == 0x01)
 | 
			
		||||
		{
 | 
			
		||||
			return "image/photoshop";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x25 && c2 == 0x21 && c3 == 0x50 && c4 == 0x53)
 | 
			
		||||
		{
 | 
			
		||||
			return "application/postscript";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xff && c2 == 0xfb && c3 == 0x30)
 | 
			
		||||
		{
 | 
			
		||||
			return "audio/mp3";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x49 && c2 == 0x44 && c3 == 0x33)
 | 
			
		||||
		{
 | 
			
		||||
			return "audio/mp3";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xAC && c2 == 0xED)
 | 
			
		||||
		{
 | 
			
		||||
			// next two bytes are version number, currently 0x00 0x05
 | 
			
		||||
			return "application/x-java-serialized-object";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == '<')
 | 
			
		||||
		{
 | 
			
		||||
			if (c2 == '!' ||
 | 
			
		||||
					((c2 == 'h' && (c3 == 't' && c4 == 'm' && c5 == 'l' || c3 == 'e' && c4 == 'a' && c5 == 'd') || (c2 == 'b' && c3 == 'o' && c4 == 'd' && c5 == 'y'))) ||
 | 
			
		||||
					((c2 == 'H' && (c3 == 'T' && c4 == 'M' && c5 == 'L' || c3 == 'E' && c4 == 'A' && c5 == 'D') || (c2 == 'B' && c3 == 'O' && c4 == 'D' && c5 == 'Y'))))
 | 
			
		||||
			{
 | 
			
		||||
				return "text/html";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l' && c6 == ' ')
 | 
			
		||||
			{
 | 
			
		||||
				return "application/xml";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// big and little endian UTF-16 encodings, with byte order mark
 | 
			
		||||
		if (c1 == 0xfe && c2 == 0xff)
 | 
			
		||||
		{
 | 
			
		||||
			if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' && c7 == 0 && c8 == 'x')
 | 
			
		||||
			{
 | 
			
		||||
				return "application/xml";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xff && c2 == 0xfe)
 | 
			
		||||
		{
 | 
			
		||||
			if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0 && c7 == 'x' && c8 == 0)
 | 
			
		||||
			{
 | 
			
		||||
				return "application/xml";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 'B' && c2 == 'M')
 | 
			
		||||
		{
 | 
			
		||||
			return "image/bmp";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x49 && c2 == 0x49 && c3 == 0x2a && c4 == 0x00)
 | 
			
		||||
		{
 | 
			
		||||
			return "image/tiff";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x4D && c2 == 0x4D && c3 == 0x00 && c4 == 0x2a)
 | 
			
		||||
		{
 | 
			
		||||
			return "image/tiff";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8')
 | 
			
		||||
		{
 | 
			
		||||
			return "image/gif";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f')
 | 
			
		||||
		{
 | 
			
		||||
			return "image/x-bitmap";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' && c5 == 'M' && c6 == '2')
 | 
			
		||||
		{
 | 
			
		||||
			return "image/x-pixmap";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 137 && c2 == 80 && c3 == 78 && c4 == 71 && c5 == 13 && c6 == 10 && c7 == 26 && c8 == 10)
 | 
			
		||||
		{
 | 
			
		||||
			return "image/png";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF)
 | 
			
		||||
		{
 | 
			
		||||
			if (c4 == 0xE0)
 | 
			
		||||
			{
 | 
			
		||||
				return "image/jpeg";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/**
 | 
			
		||||
			 * File format used by digital cameras to store images. Exif Format can be read by any application supporting JPEG. Exif Spec can be found at:
 | 
			
		||||
			 * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
 | 
			
		||||
			 */
 | 
			
		||||
			if ((c4 == 0xE1) && (c7 == 'E' && c8 == 'x' && c9 == 'i' && c10 == 'f' && c11 == 0))
 | 
			
		||||
			{
 | 
			
		||||
				return "image/jpeg";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (c4 == 0xEE)
 | 
			
		||||
			{
 | 
			
		||||
				return "image/jpg";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * According to http://www.opendesign.com/files/guestdownloads/OpenDesign_Specification_for_.dwg_files.pdf
 | 
			
		||||
		 * first 6 bytes are of type "AC1018" (for example) and the next 5 bytes are 0x00.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((c1 == 0x41 && c2 == 0x43) && (c7 == 0x00 && c8 == 0x00 && c9 == 0x00 && c10 == 0x00 && c11 == 0x00))
 | 
			
		||||
		{
 | 
			
		||||
			return "application/acad";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64)
 | 
			
		||||
		{
 | 
			
		||||
			return "audio/basic"; // .au
 | 
			
		||||
			// format,
 | 
			
		||||
			// big
 | 
			
		||||
			// endian
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E)
 | 
			
		||||
		{
 | 
			
		||||
			return "audio/basic"; // .au
 | 
			
		||||
			// format,
 | 
			
		||||
			// little
 | 
			
		||||
			// endian
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F')
 | 
			
		||||
		{
 | 
			
		||||
			/*
 | 
			
		||||
			 * I don't know if this is official but evidence suggests that .wav files start with "RIFF" - brown
 | 
			
		||||
			 */
 | 
			
		||||
			return "audio/x-wav";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (c1 == 'P' && c2 == 'K')
 | 
			
		||||
		{
 | 
			
		||||
			// its application/zip but this could be a open office thing if name is given
 | 
			
		||||
			String contentType = guessContentTypeFromName(name);
 | 
			
		||||
			if (contentType != null)
 | 
			
		||||
			{
 | 
			
		||||
				return contentType;
 | 
			
		||||
			}
 | 
			
		||||
			return "application/zip";
 | 
			
		||||
		}
 | 
			
		||||
		return guessContentTypeFromName(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static final Map<String, String> mimeTypes = new HashMap<String, String>();
 | 
			
		||||
 | 
			
		||||
	public static String guessContentTypeFromName(String name)
 | 
			
		||||
	{
 | 
			
		||||
		if (name == null) return null;
 | 
			
		||||
 | 
			
		||||
		int lastIndex = name.lastIndexOf('.');
 | 
			
		||||
		if (lastIndex != -1)
 | 
			
		||||
		{
 | 
			
		||||
			String extention = name.substring(lastIndex + 1).toLowerCase();
 | 
			
		||||
			if (mimeTypes.size() == 0)
 | 
			
		||||
			{
 | 
			
		||||
				HashMap<String, String> tempMap = new HashMap<String, String>();
 | 
			
		||||
				InputStream is = MimeTypes.class.getResourceAsStream("mime.types.properties");
 | 
			
		||||
				try
 | 
			
		||||
				{
 | 
			
		||||
					Properties properties = new Properties();
 | 
			
		||||
					properties.load(is);
 | 
			
		||||
					for (Object key : properties.keySet())
 | 
			
		||||
					{
 | 
			
		||||
						String property = properties.getProperty((String)key);
 | 
			
		||||
						StringTokenizer st = new StringTokenizer(property, " ");
 | 
			
		||||
						while (st.hasMoreTokens())
 | 
			
		||||
						{
 | 
			
		||||
							tempMap.put(st.nextToken(), (String)key);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				catch (IOException e)
 | 
			
		||||
				{
 | 
			
		||||
					//Debug.error(e);
 | 
			
		||||
				}
 | 
			
		||||
				finally
 | 
			
		||||
				{
 | 
			
		||||
					try
 | 
			
		||||
					{
 | 
			
		||||
						is.close();
 | 
			
		||||
					}
 | 
			
		||||
					catch (IOException e)
 | 
			
		||||
					{
 | 
			
		||||
						//Debug.error(e);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				synchronized (mimeTypes)
 | 
			
		||||
				{
 | 
			
		||||
					mimeTypes.putAll(tempMap);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return mimeTypes.get(extention);
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static String fileExtension(String fileName) {
 | 
			
		||||
		int index = fileName.lastIndexOf(".");
 | 
			
		||||
 | 
			
		||||
		if (index > -1) {
 | 
			
		||||
			return fileName.substring(index + 1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
package info.bukova.isspst.storage;
 | 
			
		||||
 | 
			
		||||
import info.bukova.isspst.data.FileMetainfo;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.stereotype.Controller;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.ServletOutputStream;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by pepa on 2.2.15.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@Controller
 | 
			
		||||
public class StorageController {
 | 
			
		||||
 | 
			
		||||
	@Autowired
 | 
			
		||||
	private DocumentFileStorage documentStorage;
 | 
			
		||||
	private static final Logger logger = LoggerFactory.getLogger(StorageController.class);
 | 
			
		||||
 | 
			
		||||
	@RequestMapping(value = "dl/{path}/{name}")
 | 
			
		||||
	public void viewFile(HttpServletResponse response, @PathVariable("path") String path,
 | 
			
		||||
						 @PathVariable("name") String name) {
 | 
			
		||||
		ServletOutputStream os = null;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			os = response.getOutputStream();
 | 
			
		||||
			FileMetainfo metainfo = new FileMetainfo();
 | 
			
		||||
			metainfo.setPathInFilesystem(path);
 | 
			
		||||
			byte[] data = null;
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				data = documentStorage.fileData(metainfo);
 | 
			
		||||
				metainfo.setContentType(MimeTypes.getContentType(data, path));
 | 
			
		||||
			} catch (StorageException e) {
 | 
			
		||||
				response.sendError(404);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (data != null) {
 | 
			
		||||
				response.setContentType(metainfo.getContentType());
 | 
			
		||||
				response.setContentLength(data.length);
 | 
			
		||||
 | 
			
		||||
				os.write(data, 0, data.length);
 | 
			
		||||
				os.flush();
 | 
			
		||||
				os.close();
 | 
			
		||||
			}
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			logger.error("I/O error: " + e.getMessage());
 | 
			
		||||
		} finally {
 | 
			
		||||
			if (os != null) {
 | 
			
		||||
				try {
 | 
			
		||||
					os.close();
 | 
			
		||||
				} catch (IOException e) {
 | 
			
		||||
					e.printStackTrace();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,2 @@
 | 
			
		||||
storage.root=/home/pepa/tmp/isspst
 | 
			
		||||
storage.fulltextIndex=/home/pepa/tmp/isspst
 | 
			
		||||
@ -1,57 +1,62 @@
 | 
			
		||||
<?page title="${labels.AgendaSearch}" contentType="text/html;charset=UTF-8"?>
 | 
			
		||||
<zk>
 | 
			
		||||
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 | 
			
		||||
<zk xmlns="http://www.zkoss.org/2005/zul"
 | 
			
		||||
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
    xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">
 | 
			
		||||
 | 
			
		||||
	<window border="normal" closable="false"
 | 
			
		||||
		apply="org.zkoss.bind.BindComposer"
 | 
			
		||||
		viewModel="@id('vm') @init('info.bukova.isspst.ui.search.SearchForm')"
 | 
			
		||||
		vflex="1">
 | 
			
		||||
		<caption src="/img/search-032.png" zclass="form-caption" label="${labels.AgendaSearch}" />
 | 
			
		||||
    <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 | 
			
		||||
 | 
			
		||||
    <vbox width="100%"
 | 
			
		||||
      vflex="1">
 | 
			
		||||
		  <hbox hflex="1" align="center">
 | 
			
		||||
			  <div align="center" hflex="1">
 | 
			
		||||
			    <vbox>
 | 
			
		||||
			    <image src="/img/lucene_logo.png"/>
 | 
			
		||||
					  <hbox>
 | 
			
		||||
						  <textbox value="@bind(vm.keyWord)" instant="true" width="250px" onOK="@command('doSearch')"/>
 | 
			
		||||
						  <button label="${labels.Search}" onClick="@command('doSearch')" sclass="nicebutton" image="/img/search.png" />
 | 
			
		||||
					  </hbox>
 | 
			
		||||
					</vbox>
 | 
			
		||||
				</div>
 | 
			
		||||
			</hbox>
 | 
			
		||||
			<panel hflex="1" vflex="1">
 | 
			
		||||
        <panelchildren style="overflow:auto;">
 | 
			
		||||
					<vbox children="@load(vm.results)" width="100%">
 | 
			
		||||
		        <template name="children">
 | 
			
		||||
		          <vbox width="100%">
 | 
			
		||||
		            <separator bar="true" width="100%"/>
 | 
			
		||||
		            <label value="@load(each.recordName)" style="font-weight: bold; font-size:14px;"/>
 | 
			
		||||
		            <label value="@load(each.description)"/>
 | 
			
		||||
		            <hbox>
 | 
			
		||||
		              <label value="${labels.created}: "/>
 | 
			
		||||
		              <label value="@load(each.created)"/>
 | 
			
		||||
		            </hbox>
 | 
			
		||||
		            <hbox>
 | 
			
		||||
                  <label value="${labels.ownedBy}: "/>
 | 
			
		||||
                  <label value="@load(each.ownedBy)"/>
 | 
			
		||||
                </hbox>
 | 
			
		||||
                <hbox>
 | 
			
		||||
                  <label value="${labels.modified}: "/>
 | 
			
		||||
                  <label value="@load(each.modified)"/>
 | 
			
		||||
                </hbox>
 | 
			
		||||
                <hbox>
 | 
			
		||||
                  <label value="${labels.modifiedBy}: "/>
 | 
			
		||||
                  <label value="@load(each.modifiedBy)"/>
 | 
			
		||||
                </hbox>
 | 
			
		||||
		            <a href="@load(each.url)" label="@load(each.url)"/>
 | 
			
		||||
		          </vbox>
 | 
			
		||||
		        </template>			
 | 
			
		||||
					</vbox>
 | 
			
		||||
				</panelchildren>
 | 
			
		||||
			</panel>
 | 
			
		||||
		</vbox>
 | 
			
		||||
    <window border="normal" closable="false"
 | 
			
		||||
            apply="org.zkoss.bind.BindComposer"
 | 
			
		||||
            viewModel="@id('vm') @init('info.bukova.isspst.ui.search.SearchForm')"
 | 
			
		||||
            vflex="1">
 | 
			
		||||
        <caption src="/img/search-032.png" zclass="form-caption" label="${labels.AgendaSearch}"/>
 | 
			
		||||
 | 
			
		||||
	</window>
 | 
			
		||||
        <vbox width="100%"
 | 
			
		||||
              vflex="1">
 | 
			
		||||
            <hbox hflex="1" align="center">
 | 
			
		||||
                <div align="center" hflex="1">
 | 
			
		||||
                    <vbox>
 | 
			
		||||
                        <image src="/img/lucene_logo.png"/>
 | 
			
		||||
                        <hbox>
 | 
			
		||||
                            <textbox value="@bind(vm.keyWord)" instant="true" width="250px"
 | 
			
		||||
                                     onOK="@command('doSearch')"/>
 | 
			
		||||
                            <button label="${labels.Search}" onClick="@command('doSearch')" sclass="nicebutton"
 | 
			
		||||
                                    image="/img/search.png"/>
 | 
			
		||||
                        </hbox>
 | 
			
		||||
                    </vbox>
 | 
			
		||||
                </div>
 | 
			
		||||
            </hbox>
 | 
			
		||||
            <panel hflex="1" vflex="1">
 | 
			
		||||
                <panelchildren style="overflow:auto;">
 | 
			
		||||
                    <vbox children="@load(vm.results)" width="100%">
 | 
			
		||||
                        <template name="children">
 | 
			
		||||
                            <vbox width="100%">
 | 
			
		||||
                                <separator bar="true" width="100%"/>
 | 
			
		||||
                                <label value="@load(each.recordName)" style="font-weight: bold; font-size:14px;"/>
 | 
			
		||||
                                <label value="@load(each.description)"/>
 | 
			
		||||
                                <hbox>
 | 
			
		||||
                                    <label value="${labels.created}: "/>
 | 
			
		||||
                                    <label value="@load(each.created)"/>
 | 
			
		||||
                                </hbox>
 | 
			
		||||
                                <hbox>
 | 
			
		||||
                                    <label value="${labels.ownedBy}: "/>
 | 
			
		||||
                                    <label value="@load(each.ownedBy)"/>
 | 
			
		||||
                                </hbox>
 | 
			
		||||
                                <hbox>
 | 
			
		||||
                                    <label value="${labels.modified}: "/>
 | 
			
		||||
                                    <label value="@load(each.modified)"/>
 | 
			
		||||
                                </hbox>
 | 
			
		||||
                                <hbox>
 | 
			
		||||
                                    <label value="${labels.modifiedBy}: "/>
 | 
			
		||||
                                    <label value="@load(each.modifiedBy)"/>
 | 
			
		||||
                                </hbox>
 | 
			
		||||
                                <a href="@load(each.url)" label="@load(each.url)"/>
 | 
			
		||||
                            </vbox>
 | 
			
		||||
                        </template>
 | 
			
		||||
                    </vbox>
 | 
			
		||||
                </panelchildren>
 | 
			
		||||
            </panel>
 | 
			
		||||
        </vbox>
 | 
			
		||||
 | 
			
		||||
    </window>
 | 
			
		||||
</zk>
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue