/******************************************************************************* * Copyright 2Fi Business Solutions Ltd. * * This code is copyrighted. Under no circumstances should any party, people, * or organization should redistribute any portions of this code in any form, * either verbatim or through electronic media, to any third parties, unless * under explicit written permission by 2Fi Business Solutions Ltd. ******************************************************************************/ package com.ffii.tbms.api.web; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; import com.ffii.core.setting.service.SettingsService; import com.ffii.core.utils.FileUtils; import com.ffii.core.utils.MapUtils; import com.ffii.core.utils.NumberUtils; import com.ffii.core.utils.Params; import com.ffii.core.utils.StringUtils; import com.ffii.core.utils.web.ServletRequestUtils; import com.ffii.core.web.AbstractController; import com.ffii.core.web.view.AbstractView; import com.ffii.core.web.view.json.JsonView; import com.ffii.tbms.api.service.AuthenticationService; import com.ffii.tbms.api.service.OrderApiService; import com.ffii.tbms.file.File; import com.ffii.tbms.file.FileBlob; import com.ffii.tbms.file.FileRef; import com.ffii.tbms.file.FileRefType; import com.ffii.tbms.file.service.FileService; import com.ffii.tbms.log.LogUtils; import com.ffii.tbms.log.service.AuditLogService; import com.ffii.tbms.order.Order; import com.ffii.tbms.order.service.OrderService; import org.apache.commons.lang3.RandomStringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; @Controller @RequestMapping(value = "/api/file") public class FileApiController extends AbstractController { @Autowired private AuthenticationService authenticationService; @Autowired OrderApiService orderApiService; @Autowired private FileService fileService; @Autowired private SettingsService settingsService; @Autowired private AuditLogService auditLogService; private static final long MAX_FILE_UPLOAD_SIZE = 25 * 1024 * 1024; // 25 MB private static final boolean OVERWRITE_SAME_FILENAME = false; private static final int DEFAULT_UPLOAD_MAX_FILE_SIZE_MB = 50; @RequestMapping(value = "/list.json", method = { RequestMethod.GET, RequestMethod.POST }) public String listJson(Model model, @RequestParam String deviceId, @RequestParam String token, @RequestParam String refType, @RequestParam Integer refId) { Map data = authenticationService.findUserByAccessToken(deviceId, token); if (data == null) { model.addAllAttributes(authenticationService.getNoUserFoundMap()); return JsonView.NAME; } Map args = new HashMap(); args.put(Params.REF_TYPE, refType); args.put(Params.REF_ID, refId); List> records = fileService.searchFiles(args); model.addAttribute(Params.RECORDS, records); model.addAttribute(Params.SUCCESS, Boolean.TRUE); return JsonView.NAME; } @RequestMapping(value = "/measurement-sheet/ul", method = RequestMethod.POST) public String uploadMeasurementSheetFile(HttpServletRequest request, Model model, @RequestParam String deviceId, @RequestParam String token, @RequestParam int orderId, @RequestParam MultipartFile multipartFile) throws Exception { Map userData = authenticationService.findUserByAccessToken(deviceId, token); if (userData == null) { model.addAllAttributes(authenticationService.getNoUserFoundMap()); return JsonView.NAME; } logger.info("upload file start"); Order order = orderApiService.find(orderId); String refType = FileRefType.MEASUREMENT_SHEET; String refCode = order.getCustId() + ""; Integer refId = orderId; // get file upload max file size setting int uploadMaxFileSize = settingsService.getInt("FILE.upload.maxFileSize", DEFAULT_UPLOAD_MAX_FILE_SIZE_MB) * 1024 * 1024; Boolean success = Boolean.TRUE; // only proceed if multipartFile is not null, and has file size if (multipartFile != null && multipartFile.getSize() > 0 && multipartFile.getSize() <= uploadMaxFileSize) { // DEBUG LOG logger.info("multipartFile.getSize() = " + multipartFile.getSize()); File file = new File(); file.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); file.setFilename(multipartFile.getOriginalFilename()); // file.setMimetype(multipartFile.getContentType()); file.setMimetype(FileUtils.guessMimetype(file.getFilename())); file.setFilesize(multipartFile.getSize()); file.setDescription(ServletRequestUtils.getTrimmedStringParameter(request, "description")); FileBlob fileBlob = new FileBlob(); fileBlob.setBytes(multipartFile.getBytes()); fileBlob.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); FileRef fileRef = new FileRef(); fileRef.setRefId(refId); fileRef.setRefType(refType); fileRef.setRefCode(refCode); fileRef.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); // get height and width if mimetype is png or jpeg if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) { BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes())); if (image != null) { file.setImageHeight(image.getHeight()); file.setImageWidth(image.getWidth()); } } if (OVERWRITE_SAME_FILENAME) { // search for existing file(s) with the same refType, refId, and filename List> existingFiles = fileService.searchFiles( MapUtils.toHashMap("refType", refType, "refId", refId, "filename", file.getFilename())); // delete them if found for (Map existingFile : existingFiles) { fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"), (String) existingFile.get("refType"), (String) existingFile.get("skey")); } } // create UserFile file.setSkey(RandomStringUtils.randomAlphanumeric(16)); fileService.saveFile(file); // create UserFileBlob fileBlob.setFileId(file.getId()); fileService.saveFileBlob(fileBlob); // create UserFileRef fileRef.setFileId(file.getId()); fileService.saveFileRef(fileRef); order.setFileId(file.getId()); orderApiService.saveOrUpdate(order); } else { success = Boolean.FALSE; // if not success, return msg to client model.addAttribute(Params.MSG, getMessageSourceAccessor().getMessage("Upload Failed")); } model.addAttribute(Params.SUCCESS, success); return JsonView.NAME; } @RequestMapping(value = "/ul", method = RequestMethod.POST) public String uploadFile(HttpServletRequest request, Model model, @RequestParam String deviceId, @RequestParam String token, @RequestParam int refId, @RequestParam String refType, @RequestParam(defaultValue = StringUtils.EMPTY) String refCode, @RequestParam MultipartFile multipartFile) throws IOException { Map userData = authenticationService.findUserByAccessToken(deviceId, token); if (userData == null) { model.addAllAttributes(authenticationService.getNoUserFoundMap()); return JsonView.NAME; } // only proceed if multipartFile is not null, and has file size if(multipartFile == null || multipartFile.getSize() <= 0 || multipartFile.getSize() > MAX_FILE_UPLOAD_SIZE){ model.addAttribute(Params.MSG, getMessageSourceAccessor().getMessage("FILE.msg.uploadFailed")); model.addAttribute(Params.SUCCESS, Boolean.FALSE); return JsonView.NAME; } // DEBUG LOG logger.info("multipartFile.getSize() = " + multipartFile.getSize()); File file = new File(); file.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); file.setFilename(multipartFile.getOriginalFilename()); // file.setMimetype(multipartFile.getContentType()); file.setMimetype(FileUtils.guessMimetype(file.getFilename())); file.setFilesize(multipartFile.getSize()); FileBlob fileBlob = new FileBlob(); fileBlob.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); fileBlob.setBytes(multipartFile.getBytes()); FileRef fileRef = new FileRef(); fileRef.setSysGroupId(NumberUtils.toInt(userData.get("sysGroupId"),0)); fileRef.setRefId(refId); fileRef.setRefType(refType); fileRef.setRefCode(refCode); // get height and width if mimetype is png or jpeg if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) { BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes())); if (image != null) { file.setImageHeight(image.getHeight()); file.setImageWidth(image.getWidth()); } } // search for existing file(s) with the same refType, refId, and filename List> existingFiles = fileService.searchFiles( MapUtils.toHashMap("refType", refType, "refId", refId, "filename", file.getFilename())); // delete them if found for (Map existingFile : existingFiles) { fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"), (String) existingFile.get("refType"), (String) existingFile.get("skey")); } // create UserFile file.setSkey(RandomStringUtils.randomAlphanumeric(16)); fileService.saveFile(file); // create UserFileBlob fileBlob.setFileId(file.getId()); fileService.saveFileBlob(fileBlob); // create UserFileRef fileRef.setFileId(file.getId()); fileService.saveFileRef(fileRef); Map map = new HashMap<>(); map.put("skey", file.getSkey()); map.put("fileId", file.getId()); map.put("filename", file.getFilename()); map.put("filesize", file.getFilesize()); map.put("refType", fileRef.getRefType()); map.put("refId", fileRef.getRefId()); map.put("mimetype", file.getMimetype()); model.addAttribute(Params.DATA, map); if(refType.equals(FileRefType.ORDER)){ ObjectMapper mapper = new ObjectMapper(); auditLogService.save("orders", refId, NumberUtils.intValue(userData.get("id")), new Date(), LogUtils.ACTION_CREATE, mapper.writeValueAsString(MapUtils.toHashMap("photoFileId", file.getId(), "filename",file.getFilename()))); } model.addAttribute(Params.AUTH, Boolean.TRUE); model.addAttribute(Params.SUCCESS, Boolean.TRUE); return JsonView.NAME; } @RequestMapping(value = "/dl/{id}/{skey}/{filename}", method = RequestMethod.GET) public void download(HttpServletResponse response, Model model, @RequestParam(defaultValue = "false") boolean dl, @PathVariable int id, @PathVariable String skey) throws IOException { File file = fileService.findFileByIdAndKey(id, skey); if (file != null) { FileBlob fileBlob = fileService.findFileBlobByFileId(id); response.reset(); response.setContentType(file.getMimetype()); response.setContentLength((int) file.getFilesize()); response.setHeader("Content-Transfer-Encoding", "binary"); response.setHeader("Content-Disposition", String.format("%s; filename=\"%s\"", dl ? "attachment" : "inline", response.encodeURL(file.getFilename()))); ServletOutputStream out = response.getOutputStream(); try { out.write(fileBlob.getBytes()); out.flush(); out.close(); } catch (IOException e) { logger.warn(e.getMessage()); } finally { out.close(); model.addAttribute(Params.SUCCESS, Boolean.TRUE); } } else { logger.info("*** 400 BAD REQUEST ***"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); model.addAttribute(Params.SUCCESS, Boolean.FALSE); } } @RequestMapping(value = "/delete", method = RequestMethod.POST) public String delete(HttpServletResponse response, Model model, @RequestParam String deviceId, @RequestParam String token, @RequestParam Integer fileId, @RequestParam Integer refId, @RequestParam String refType, @RequestParam String skey) throws IOException { Map userData = authenticationService.findUserByAccessToken(deviceId, token); if (userData == null) { model.addAllAttributes(authenticationService.getNoUserFoundMap()); return JsonView.NAME; } fileService.deleteFile(fileId, refId, refType, skey); if(refType.equals(FileRefType.MEASUREMENT_SHEET)){ ObjectMapper mapper = new ObjectMapper(); auditLogService.save("orders", refId, NumberUtils.intValue(userData.get("id")), new Date(), LogUtils.ACTION_DELETE, mapper.writeValueAsString(MapUtils.toHashMap("fileId", fileId))); }else if(refType.equals(FileRefType.ORDER)){ ObjectMapper mapper = new ObjectMapper(); auditLogService.save("orders", refId, NumberUtils.intValue(userData.get("id")), new Date(), LogUtils.ACTION_DELETE, mapper.writeValueAsString(MapUtils.toHashMap("photoFileId", fileId))); } model.addAttribute(Params.SUCCESS, Boolean.TRUE); return JsonView.NAME; } @RequestMapping(value = "/thumbnail/{id}/{skey}/{filename}", method = RequestMethod.GET) public void thumbnail(HttpServletResponse response, Model model, @RequestParam(defaultValue = "false") boolean dl, @PathVariable int id, @PathVariable String skey) throws IOException { File file = fileService.findFileByIdAndKey(id, skey); if (file != null) { FileBlob fileBlob = fileService.findFileBlobByFileId(id); response.reset(); response.setContentType(file.getMimetype()); // response.setContentLength((int) file.getFilesize()); response.setHeader("Content-Transfer-Encoding", "binary"); response.setHeader("Content-Disposition", String.format("%s; filename=\"%s\"", dl ? "attachment" : "inline", response.encodeURL(file.getFilename()))); int limit = 500; BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes())); int width = image.getWidth(); int height = image.getHeight(); if (width > height) { if (width > limit) { height = height * limit / width; width = limit; } } else { if (height > limit) { width = width * limit / height; height = limit; } } image = scale(image, width, height); ByteArrayOutputStream tmp = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", tmp); tmp.close(); response.setContentLength((int) tmp.size()); ServletOutputStream out = response.getOutputStream(); try { out.write(tmp.toByteArray()); out.flush(); out.close(); } catch (IOException e) { logger.warn(e.getMessage()); } finally { out.close(); model.addAttribute(Params.SUCCESS, Boolean.TRUE); } } else { logger.info("*** 400 BAD REQUEST ***"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); model.addAttribute(Params.SUCCESS, Boolean.FALSE); } } static BufferedImage scale(BufferedImage originalImage, int w, int h) { BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int x, y; int ww = originalImage.getWidth(); int hh = originalImage.getHeight(); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { int col = originalImage.getRGB(x * ww / w, y * hh / h); img.setRGB(x, y, col); } } return img; } }