FilesServiceImpl.java

/*
 * Copyright 2019 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gringlobal.compatibility.service.impl;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;

import lombok.extern.slf4j.Slf4j;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.NoSuchRepositoryFileException;
import org.genesys.filerepository.model.ImageGallery;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.filerepository.model.RepositoryImage;
import org.genesys.filerepository.service.BytesStorageService;
import org.genesys.filerepository.service.ImageGalleryService;
import org.genesys.filerepository.service.RepositoryService;
import org.genesys.filerepository.service.impl.RepositoryServiceImpl;
import org.gringlobal.compatibility.service.FilesService;
import org.gringlobal.model.AccessionInvAttach;
import org.gringlobal.service.InventoryAttachmentService;
import org.gringlobal.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class FilesServiceImpl implements FilesService {

	@Autowired
	@Lazy
	private InventoryService inventoryService;

	@Autowired
	@Lazy
	private InventoryAttachmentService attachmentFileService;

	@Autowired
	private RepositoryService repositoryService;
	
	@Autowired
	private ImageGalleryService imageGalleryService;
	
	@Autowired
	private BytesStorageService byteStorageService;
	
	@Value("${file.repository.image.thumbnailSizes}")
	private int[] thumbnailSizes;


	@Override
	@PreAuthorize("isAuthenticated()")
	@Transactional
	public String upload(String fullPath, byte[] imageBytes, boolean createThumbnail, boolean overwriteIfExists) throws InvalidRepositoryPathException, InvalidRepositoryFileDataException, IOException {
		Path filePath = Paths.get("/", fullPath).normalize().toAbsolutePath();
		log.info("Uploading {}b to {} {}", imageBytes.length, filePath.getParent().toAbsolutePath(), filePath.getFileName());
		log.info("Creating thumbnail={} overwrite={}", createThumbnail, overwriteIfExists);

		RepositoryFile metaData = new RepositoryFile();
		String detectedContentType = null;
		if (filePath.getFileName().toString().toLowerCase().endsWith(".png")) {
			detectedContentType = MediaType.IMAGE_PNG_VALUE;
		} else if (filePath.getFileName().toString().toLowerCase().endsWith(".jpeg")) {
			detectedContentType = MediaType.IMAGE_JPEG_VALUE;
		} else if (filePath.getFileName().toString().toLowerCase().endsWith(".jpg")) {
			detectedContentType = MediaType.IMAGE_JPEG_VALUE;
		} else if (filePath.getFileName().toString().toLowerCase().endsWith(".gif")) {
			detectedContentType = MediaType.IMAGE_GIF_VALUE;
		}
		var contentType = detectedContentType;

		RepositoryFile repositoryFile = null;

		//
		// Create or update *Attach records
		//
		if (Objects.equals("AIA", filePath.getName(0).toString()) && filePath.getNameCount() == 6) {
			// AccessionInvAttach

			// /AIA/Abarema/74/35174/35359/medium-40627.jpg
			var inventoryId = Long.parseLong(filePath.getName(4).toString());
			log.info("Have attachment {} for inventory id={}", filePath, inventoryId);

			var inventory = inventoryService.get(inventoryId); // Throws

			repositoryFile = repositoryService.addFile(filePath.getParent().toAbsolutePath(), filePath.getFileName().toString(), contentType, imageBytes, metaData);

			log.warn("Added type {} to repository", repositoryFile.getClass());

			AccessionInvAttach existingAttach = attachmentFileService.findAttachment(inventory, filePath);

			if (existingAttach != null) {
				log.info("Updating existing AccessionInvAttach {}", existingAttach);
				if (existingAttach.getRepositoryFile() != null) {
					try {
						repositoryService.removeFile(existingAttach.getRepositoryFile());
					} catch (NoSuchRepositoryFileException | IOException e) {
						log.warn("Could not remove repository file of existing attachment: {}", e.getMessage());
					}
				}
				existingAttach.setRepositoryFile(repositoryFile);
				existingAttach.setContentType(detectedContentType);
				attachmentFileService.update(existingAttach);

			} else {
				log.info("Creating new AccessionInvAttach for inventory {}", inventory);
				var newAttach = new AccessionInvAttach();
				newAttach.setInventory(inventory);
				newAttach.setRepositoryFile(repositoryFile);
				newAttach.setContentType(detectedContentType);
				newAttach.setVirtualPath(filePath.toString());
				newAttach.setTitle(filePath.getFileName().toString());
				newAttach = attachmentFileService.create(newAttach);
			}

		} else {
			// Allow uploads for non-/AIA/ folders
			repositoryFile = repositoryService.addFile(filePath.getParent().toAbsolutePath(), filePath.getFileName().toString(), contentType, imageBytes, metaData);

			log.warn("Added type {} to repository", repositoryFile.getClass());
		}

		if (createThumbnail) {
			// Thumbnails requested, it's an image gallery
			ImageGallery imageGallery = imageGalleryService.createImageGallery(filePath.getParent().toAbsolutePath(), null, null);
			imageGalleryService.ensureThumbnails(imageGallery);
		}

		// CT Wizard checks if the return message contains the file name
		return Path.of(repositoryFile.getFolder().getPath(), repositoryFile.getOriginalFilename()).toString();
	}

	@Override
	@PreAuthorize("isAuthenticated()")
	public byte[] download(String fullPath) throws NoSuchRepositoryFileException, InvalidRepositoryPathException, IOException {
		Path filePath = Paths.get("/", fullPath);
		log.info("Getting {} {}", filePath.getParent().toAbsolutePath(), filePath.getFileName());

		RepositoryFile repositoryFile = repositoryService.getFile(filePath.getParent().toAbsolutePath(), filePath.getFileName().toString());
		return repositoryService.getFileBytes(repositoryFile);
	}

	@Override
	@PreAuthorize("isAuthenticated()")
	public boolean delete(String fullPath) throws NoSuchRepositoryFileException, InvalidRepositoryPathException, IOException {
		Path filePath = Paths.get("/", fullPath);
		log.info("Deleting {} {}", filePath.getParent().toAbsolutePath(), filePath.getFileName());

		RepositoryFile repositoryFile = repositoryService.getFile(filePath.getParent().toAbsolutePath(), filePath.getFileName().toString());
		repositoryService.removeFile(repositoryFile);
		return true;
	}

	@Override
	public byte[] thumbnail(String fullPath) throws NoSuchRepositoryFileException, InvalidRepositoryPathException, IOException {
		Path filePath = Paths.get("/", fullPath);
		Path imagePath = Paths.get(filePath.getParent().toString(), filePath.getFileName().toString().replace("_thumbnail", ""));
		log.info("Thumb path {}", imagePath.toAbsolutePath());
		RepositoryFile repositoryImage = repositoryService.getFile(imagePath.getParent(), imagePath.getFileName().toString());
		if (repositoryImage instanceof RepositoryImage) {
			Path thumbPath = RepositoryServiceImpl.getFullThumbnailsPath((RepositoryImage) repositoryImage);
			// Take the middle-sized thumbnail :-)
			Path thumbFile = Paths.get(thumbPath.toString(), thumbnailSizes[thumbnailSizes.length/2] + "x" + thumbnailSizes[thumbnailSizes.length/2] + ".jpg");
			return byteStorageService.get(thumbFile);
		}
		log.info("No thumb in {} for {}", imagePath.getParent(), imagePath.getFileName());
		throw new NoSuchRepositoryFileException("No image for this path.");
	}
}