package fr.ippon.tatami.web.fileupload; import com.yammer.metrics.annotation.Timed; import fr.ippon.tatami.domain.Attachment; import fr.ippon.tatami.domain.Avatar; import fr.ippon.tatami.domain.User; import fr.ippon.tatami.repository.UserRepository; import fr.ippon.tatami.security.AuthenticationService; import fr.ippon.tatami.service.AttachmentService; import fr.ippon.tatami.service.AvatarService; import fr.ippon.tatami.service.UserService; import fr.ippon.tatami.service.exception.StorageSizeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Date; import java.util.List; @Controller public class FileController { private static final Logger log = LoggerFactory.getLogger(FileController.class); private static final String HEADER_EXPIRES = "Expires"; private static final String HEADER_CACHE_CONTROL = "Cache-Control"; private static final int CACHE_SECONDS = 60 * 60 * 24 * 30; private static final String HEADER_ETAG = "ETag"; private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; private String tatamiUrl; @Inject private Environment env; @Inject private AttachmentService attachmentService; @Inject private AvatarService avatarService; @Inject private UserService userService; @Inject private UserRepository userRepository; @Inject private AuthenticationService authenticationService; @PostConstruct public void init() { this.tatamiUrl = env.getProperty("tatami.url"); } @RequestMapping(value = "/file/{attachmentId}/*", method = RequestMethod.GET) @Timed public void download(@PathVariable("attachmentId") String attachmentId, HttpServletRequest request, HttpServletResponse response) throws IOException { // Cache the file in the browser response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + CACHE_SECONDS * 1000L); response.setHeader(HEADER_CACHE_CONTROL, "max-age=" + CACHE_SECONDS + ", must-revalidate"); // Put the file in the response Attachment attachment = attachmentService.getAttachmentById(attachmentId); if (attachment == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.sendRedirect("/tatami/file/file_not_found"); } else { // ETag support response.setHeader(HEADER_ETAG, attachmentId); // The attachmentId is unique and should not be modified String requestETag = request.getHeader(HEADER_IF_NONE_MATCH); if (requestETag != null && requestETag.equals(attachmentId)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { try { byte[] fileContent = attachment.getContent(); response.getOutputStream().write(fileContent); } catch (IOException e) { log.info("Error writing file to output stream. {}", e.getMessage()); } } } try { response.flushBuffer(); } catch (IOException e) { log.info("Error flushing the output stream. {}", e.getMessage()); } } @RequestMapping(value = "/thumbnail/{attachmentId}/*", method = RequestMethod.GET) @Timed public void thumbnail(@PathVariable("attachmentId") String attachmentId, HttpServletRequest request, HttpServletResponse response) throws IOException { // Cache the file in the browser response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + CACHE_SECONDS * 1000L); response.setHeader(HEADER_CACHE_CONTROL, "max-age=" + CACHE_SECONDS + ", must-revalidate"); // Put the file in the response Attachment attachment = attachmentService.getAttachmentById(attachmentId); if (attachment == null || attachment.getThumbnail().length == 0) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.sendRedirect("/tatami/file/file_not_found"); } else { // ETag support response.setHeader(HEADER_ETAG, attachmentId); // The attachmentId is unique and should not be modified String requestETag = request.getHeader(HEADER_IF_NONE_MATCH); if (requestETag != null && requestETag.equals(attachmentId)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { try { byte[] fileContent = attachment.getThumbnail(); response.getOutputStream().write(fileContent); } catch (IOException e) { log.info("Error writing file to output stream. {}", e.getMessage()); } } } try { response.flushBuffer(); } catch (IOException e) { log.info("Error flushing the output stream. {}", e.getMessage()); } } @RequestMapping(value = "/avatar/{avatarId}/*", method = RequestMethod.GET) @Timed public void getAvatar(@PathVariable("avatarId") String avatarId, HttpServletRequest request, HttpServletResponse response) throws IOException { // Cache the file in the browser response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + CACHE_SECONDS * 1000L); response.setHeader(HEADER_CACHE_CONTROL, "max-age=" + CACHE_SECONDS + ", must-revalidate"); // Put the file in the response Avatar avatar = avatarService.getAvatarById(avatarId); if (avatarId == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); } else { // ETag support response.setHeader(HEADER_ETAG, avatarId); // The attachmentId is unique and should not be modified String requestETag = request.getHeader(HEADER_IF_NONE_MATCH); if (requestETag != null && requestETag.equals(avatarId)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { try { byte[] fileContent = avatar.getContent(); response.getOutputStream().write(fileContent); } catch (IOException e) { log.info("Error writing file to output stream. {}", e.getMessage()); } } } try { response.flushBuffer(); } catch (IOException e) { log.info("Error flushing the output stream. {}", e.getMessage()); } } @RequestMapping(value = "/rest/fileupload/avatar", method = RequestMethod.POST) @ResponseBody @Timed public List<UploadedFile> uploadAvatar( @RequestParam("uploadFile") MultipartFile file) throws IOException { Avatar avatar = new Avatar(); avatar.setContent(file.getBytes()); avatar.setFilename(file.getOriginalFilename()); avatar.setSize(file.getSize()); avatar.setCreationDate(new Date()); avatarService.createAvatar(avatar); List<UploadedFile> uploadedFiles = new ArrayList<UploadedFile>(); UploadedFile uploadedFile = new UploadedFile( avatar.getAvatarId(), file.getOriginalFilename(), Long.valueOf(file.getSize()).intValue(), tatamiUrl + "/tatami/avatar/" + avatar.getAvatarId() + "/" + file.getOriginalFilename()); log.info("Avatar url : {}/tatami/avatar/{}/{}", tatamiUrl, avatar.getAvatarId(), file.getOriginalFilename()); uploadedFiles.add(uploadedFile); User user = authenticationService.getCurrentUser(); user.setAvatar(avatar.getAvatarId()); userRepository.updateUser(user); return uploadedFiles; } @RequestMapping(value = "/rest/fileupload/avatarIE", headers = "content-type=multipart/*", method = RequestMethod.POST) @ResponseBody @Timed public void uploadAvatarIE( @RequestParam("uploadFile") MultipartFile file) throws IOException { Avatar avatar = new Avatar(); avatar.setContent(file.getBytes()); avatar.setFilename(file.getOriginalFilename()); avatar.setSize(file.getSize()); avatar.setCreationDate(new Date()); avatarService.createAvatar(avatar); log.info("Avatar url : {}/tatami/avatar/{}/{}", tatamiUrl, avatar.getAvatarId(), file.getOriginalFilename()); User user = authenticationService.getCurrentUser(); user.setAvatar(avatar.getAvatarId()); userRepository.updateUser(user); } @RequestMapping(value = "/rest/fileupload", method = RequestMethod.POST) @ResponseBody @Timed public List<UploadedFile> upload(@RequestParam("uploadFile") MultipartFile file) throws IOException, StorageSizeException { Attachment attachment = new Attachment(); attachment.setContent(file.getBytes()); attachment.setFilename(file.getName()); attachment.setSize(file.getSize()); attachment.setFilename(file.getOriginalFilename()); attachment.setCreationDate(new Date()); attachmentService.createAttachment(attachment); log.debug("Created attachment : {}", attachment.getAttachmentId()); List<UploadedFile> uploadedFiles = new ArrayList<UploadedFile>(); UploadedFile uploadedFile = new UploadedFile( attachment.getAttachmentId(), file.getOriginalFilename(), Long.valueOf(file.getSize()).intValue(), tatamiUrl + "/tatami/file/" + attachment.getAttachmentId() + "/" + file.getOriginalFilename()); uploadedFiles.add(uploadedFile); return uploadedFiles; } @RequestMapping(value = "/rest/fileuploadIE", headers = "content-type=multipart/*", method = RequestMethod.POST, produces = "text/html") @ResponseBody @Timed public String uploadIE(@RequestParam("uploadFile") MultipartFile file) throws IOException, StorageSizeException { Attachment attachment = new Attachment(); attachment.setContent(file.getBytes()); attachment.setFilename(file.getName()); attachment.setSize(file.getSize()); attachment.setFilename(file.getOriginalFilename()); attachment.setCreationDate(new Date()); attachmentService.createAttachment(attachment); log.debug("Created attachment : {}", attachment.getAttachmentId()); String result = attachment.getAttachmentId()+":::"+file.getOriginalFilename()+":::"+file.getSize(); return URLEncoder.encode(result, "UTF-8"); } @RequestMapping(value = "/file/file_not_found", method = RequestMethod.GET) @Timed public ModelAndView FileNotFound() { log.debug("File not found !"); return new ModelAndView("errors/file_not_found"); } }