Merge branch 'master' of https://git.bukova.info/repos/git/isspst
Conflicts: src/main/webapp/app/mainMenu.zulVerze_2.0
						commit
						93e1ef1fd3
					
				| @ -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"?> | <?page title="${labels.AgendaSearch}" contentType="text/html;charset=UTF-8"?> | ||||||
| <zk> | <zk xmlns="http://www.zkoss.org/2005/zul" | ||||||
| <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> |     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" |     <?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?> | ||||||
| 		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}" /> |  | ||||||
| 
 | 
 | ||||||
|     <vbox width="100%" |     <window border="normal" closable="false" | ||||||
|       vflex="1"> |             apply="org.zkoss.bind.BindComposer" | ||||||
| 		  <hbox hflex="1" align="center"> |             viewModel="@id('vm') @init('info.bukova.isspst.ui.search.SearchForm')" | ||||||
| 			  <div align="center" hflex="1"> |             vflex="1"> | ||||||
| 			    <vbox> |         <caption src="/img/search-032.png" zclass="form-caption" label="${labels.AgendaSearch}"/> | ||||||
| 			    <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> |         <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> | </zk> | ||||||
					Loading…
					
					
				
		Reference in New Issue