Java tutorial
/* fEMR - fast Electronic Medical Records Copyright (C) 2014 Team fEMR fEMR is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. fEMR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with fEMR. If not, see <http://www.gnu.org/licenses/>. If you have any questions, contact <info@teamfemr.org>. */ package femr.business.services.system; import com.avaje.ebean.Ebean; import com.google.inject.name.Named; import femr.business.helpers.LogicDoer; import femr.business.services.core.IPhotoService; import femr.common.IItemModelMapper; import femr.common.models.PatientEncounterItem; import femr.common.dtos.ServiceResponse; import com.google.inject.Inject; import femr.common.models.PhotoItem; import femr.data.daos.core.IPatientRepository; import femr.data.daos.core.IPhotoRepository; import femr.data.models.core.IPatient; import femr.data.models.core.IPatientEncounterPhoto; import femr.data.models.core.IPhoto; import femr.ui.models.medical.EditViewModelPost; import femr.util.stringhelpers.StringUtils; import org.apache.commons.codec.binary.Base64; import play.Logger; import play.mvc.Http.MultipartFormData.FilePart; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.nio.charset.Charset; import java.nio.file.*; import java.util.ArrayList; import java.util.List; import java.io.ByteArrayOutputStream; public class PhotoService implements IPhotoService { private String _profilePhotoPath; private String _encounterPhotoPath; private IPhotoRepository photoRepository; private IPatientRepository patientRepository; private final IItemModelMapper itemModelMapper; private boolean _bUseDbPhotoStorage; @Inject public PhotoService(IPatientRepository patientRepository, IPhotoRepository photoRepository, @Named("identified") IItemModelMapper itemModelMapper) { this.photoRepository = photoRepository; this.patientRepository = patientRepository; this.itemModelMapper = itemModelMapper; this.Init(); } private void Init() { File f; _profilePhotoPath = LogicDoer.getPatientPhotoPath(); _encounterPhotoPath = LogicDoer.getMedicalPhotoPath(); _bUseDbPhotoStorage = LogicDoer.getUseDbStorageFlag(); if (!_bUseDbPhotoStorage) { //Ensure folder exists, if not, create it f = new File(_profilePhotoPath); if (!f.exists()) f.mkdirs(); f = new File(_encounterPhotoPath); if (!f.exists()) f.mkdirs(); } } private byte[] convertBufferedImageToByteArray(BufferedImage img) { if (img != null) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(img, "jpg", baos); return baos.toByteArray(); } catch (Exception ex) { } } return null; } /** * {@inheritDoc} */ @Override public ServiceResponse<Boolean> createPatientPhoto(String imageString, int patientId, Boolean deleteFlag) { ServiceResponse<Boolean> response = new ServiceResponse<>(); try { IPatient patient = patientRepository.retrievePatientById(patientId); if (StringUtils.isNotNullOrWhiteSpace(imageString)) { //Decode image, save as BufferedImage: String parsedImage = imageString.substring(imageString.indexOf(",") + 1); BufferedImage bufferedImage = decodeToImage(parsedImage); String imageFileName = "N/A"; if (!_bUseDbPhotoStorage) { //Store to file system imageFileName = "/Patient_" + patient.getId() + ".jpg"; //save image to disk String filePathTarget = _profilePhotoPath + imageFileName; photoRepository.createPhotoOnFilesystem(bufferedImage, filePathTarget); } if (patient.getPhoto() == null) { //Create new photo Id record, pass in binary photo data if _bUseDbPhotoStorage is true IPhoto pPhoto = photoRepository.createPhoto("", imageFileName, _bUseDbPhotoStorage ? convertBufferedImageToByteArray(bufferedImage) : null); patient.setPhoto(pPhoto); patientRepository.savePatient(patient); } else { //Record already exists: //photoId = patient.getPhoto().getId(); } } else { if (deleteFlag != null) if (deleteFlag && patient.getPhoto() != null) { //delete photo //First make sure the photoId is null in the patient record Integer id = patient.getPhoto().getId(); patient.setPhoto(null); patientRepository.savePatient(patient); //Now remove the photo record: this.deletePhotoById(id, _profilePhotoPath); } } response.setResponseObject(true); } catch (Exception ex) { response.setResponseObject(false); response.addError("HandlePatientPhoto", ex.getMessage()); } return response; } @Override public ServiceResponse<byte[]> retrievePhotoData(int photoId) { ServiceResponse<byte[]> response = new ServiceResponse<>(); try { IPhoto photo = photoRepository.retrievePhotoById(photoId); if (photo != null) { if (!_bUseDbPhotoStorage) { //Read data from file and return: File encPhoto = new File(_encounterPhotoPath + photo.getFilePath()); if (encPhoto.canRead()) { byte[] photoData = Files.readAllBytes(Paths.get(_encounterPhotoPath + photo.getFilePath())); response.setResponseObject(photoData); } } else { //Data is already loaded in Photo record, let's return it! response.setResponseObject(photo.getPhotoBlob()); } } } catch (Exception ex) { response.addError("", ex.getMessage()); return response; } return response; } @Override public ServiceResponse<byte[]> retrievePatientPhotoData(int patientId) { ServiceResponse<byte[]> response = new ServiceResponse<>(); try { IPatient patient = patientRepository.retrievePatientById(patientId); if (patient.getPhoto() == null) { response.setResponseObject(null); } else { if (_bUseDbPhotoStorage) { //BLOB Mode response.setResponseObject(patient.getPhoto().getPhotoBlob()); } else { //File system mode byte[] photoData = Files .readAllBytes(Paths.get(_profilePhotoPath + patient.getPhoto().getFilePath())); response.setResponseObject(photoData); } } } catch (Exception ex) { response.addError("", ex.getMessage()); return response; } return response; } /** * {@inheritDoc} */ @Override public ServiceResponse<Boolean> createEncounterPhotos(List<FilePart<File>> encounterImages, PatientEncounterItem patientEncounterItem, EditViewModelPost mod) { ServiceResponse<Boolean> sr = new ServiceResponse<>(); try { int count = mod.getPhotoId().size(); FilePart fp; for (Integer i = 0; i < count; i++) { Integer id = mod.getPhotoId().get(i); if (id == null) { //This is a new image, add it to the DB and filesystem: for (FilePart<File> fp_iterator : encounterImages) { String keyName = fp_iterator.getKey(); int leftBracket = keyName.indexOf("["); int rightBracket = keyName.indexOf("]"); if (leftBracket >= 0 && rightBracket >= 0) { keyName = keyName.substring(leftBracket + 1, rightBracket); String tempindex = i.toString(); if (keyName.equalsIgnoreCase(tempindex)) { saveNewEncounterImage(fp_iterator.getFile(), patientEncounterItem, mod.getImageDescText().get(i)); break; } } } } else { Boolean bDelete = mod.getDeleteRequested().get(i); if (bDelete == null) bDelete = false; if (bDelete) { //Delete image if (mod.getPhotoId().get(i) != null) this.deletePhotoById(mod.getPhotoId().get(i), _encounterPhotoPath); } else { //Possibly update the image Boolean bisUpdate = mod.getHasUpdatedDesc().get(i); if (bisUpdate != null) { if (bisUpdate) { Integer photoId = mod.getPhotoId().get(i); if (photoId != null) { //updateImageDescription(photoId, mod.getImageDescText().get(i)); try { photoRepository.updatePhotoDescription(photoId, mod.getImageDescText().get(i)); } catch (Exception ex) { sr.addError("", "error updating description: " + ex.getMessage()); } } } } } } } sr.setResponseObject(true); } catch (Exception ex) { sr.setResponseObject(false); sr.addError("createEncounterPhotos()", ex.getMessage()); } return sr; } /** * {@inheritDoc} */ @Override public ServiceResponse<List<PhotoItem>> retrieveEncounterPhotos(int encounterId) { ServiceResponse<List<PhotoItem>> response = new ServiceResponse<>(); try { List<? extends IPatientEncounterPhoto> photoList = photoRepository .retrieveEncounterPhotosByEncounterId(encounterId); if (photoList != null) { List<PhotoItem> returnList = new ArrayList<>(); for (IPatientEncounterPhoto pep : photoList) { try { IPhoto savedPhoto = photoRepository.retrievePhotoById(pep.getPhotoId()); returnList.add( itemModelMapper.createPhotoItem(savedPhoto.getId(), savedPhoto.getDescription(), savedPhoto.getInsertTS(), femr.ui.controllers.routes.PhotoController .GetPhoto(savedPhoto.getId()).toString())); } catch (Exception ex) { response.addError("", ex.getMessage()); return response; } } response.setResponseObject(returnList); } } catch (Exception ex) { response.addError("photo", ex.getMessage()); } return response; } private void saveNewEncounterImage(File image, PatientEncounterItem patientEncounter, String descriptionText) { try { String imageFileName = "N/A"; //Create photo record: IPhoto pPhoto = photoRepository.createPhoto(descriptionText, "", null); //this is redundant...? IPhoto editPhoto = photoRepository.retrievePhotoById(pPhoto.getId()); if (!_bUseDbPhotoStorage) { //Build the filepath for the image imageFileName = "Patient_" + patientEncounter.getPatientItem().getId() + "_Enc_" + patientEncounter.getId() + "_Photo_" + editPhoto.getId(); } //Since the photo ID is part of the file name // I am setting the filePath field after the record is created photoRepository.updatePhotoFilePath(editPhoto.getId(), imageFileName); //Link photo record in photoEncounter table photoRepository.createEncounterPhoto(editPhoto.getId(), patientEncounter.getId()); if (!_bUseDbPhotoStorage) { photoRepository.createPhotoOnFilesystem(image, this._encounterPhotoPath + imageFileName); } else { //Read image data, update blob field: byte[] imgData = Files.readAllBytes(Paths.get(image.getAbsolutePath())); photoRepository.updatePhotoData(pPhoto.getId(), imgData); } } catch (Exception ex) { String test = "uh oh"; } } /** * Deletes a photo from the filesystem. * * @param id id of the photo * @param filePath directory the photo is in * @return */ private ServiceResponse<IPhoto> deletePhotoById(int id, String filePath) { IPhoto savedPhoto = photoRepository.retrievePhotoById(id); if (savedPhoto != null) { photoRepository.deleteEncounterPhotosByPhotoId(savedPhoto.getId()); if (!_bUseDbPhotoStorage) photoRepository.deletePhotoFromFilesystemById(filePath + savedPhoto.getFilePath()); photoRepository.deletePhotoById(savedPhoto.getId()); } ServiceResponse<IPhoto> response = new ServiceResponse<>(); if (savedPhoto != null) { response.setResponseObject(savedPhoto); } else { response.addError("photo", "photo could not be deleted from the database"); } return response; } /** * Decodes a base64 encoded string to an image * * @param imageString base64 encoded string that has been parsed to only include imageBytes * @return the decoded image */ private static BufferedImage decodeToImage(String imageString) { BufferedImage image = null; byte[] imageByte; try { Base64 newDecoder = new Base64(); byte[] bytes = imageString.getBytes(Charset.forName("UTF-8")); imageByte = newDecoder.decode(bytes); ByteArrayInputStream bis = new ByteArrayInputStream(imageByte); image = ImageIO.read(bis); bis.close(); } catch (Exception e) { e.printStackTrace(); } return image; } }