diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java index ad6beed..324ec94 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; @@ -146,4 +147,24 @@ public class HorseEndpoint { "Something went wrong while uploading the file"); } } + + @RequestMapping(value = "/image/{path}", produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_PNG_VALUE}, method=RequestMethod.GET) + @ResponseStatus(HttpStatus.OK) + public @ResponseBody byte[] getImage(@PathVariable("path") String path) { + // Get the image as byte array and return it with a media type assigned + // https://www.baeldung.com/spring-controller-return-image-file + // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html + LOGGER.info("GET " + BASE_URL + "/image/{}", path); + try { + return horseService.getImage(path); + } catch(ValidationException e) { + LOGGER.error(e.getMessage()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, + "Unsupported file type provided. Supported file types are jpg and png."); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, + "Something went wrong while reading the file"); + } + } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java index af44628..da175a8 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java @@ -18,4 +18,12 @@ public interface FileDao { * @throws IOException if something goes wrong with deleting the file */ void delete(String fileName) throws IOException; + + /** + * Used fot getting a file from the local filesystem + * @param fileName of the file to open + * @return the file as bytes + * @throws IOException if something goes wrong during file access + */ + byte[] get(String fileName) throws IOException; } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java index 0e45e33..df9d97b 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java @@ -63,4 +63,21 @@ public class HorseFileDao implements FileDao { throw new IOException("Deleting the file specified failed"); } } + + @Override + public byte[] get(String fileName) throws IOException { + if(fileName == null || fileName.equals("")) + throw new IOException("Cannot open an unexisting file"); + + LOGGER.trace("Getting file from " + FILE_BASE_PATH + fileName); + try { + // Read the file and return a byte array + // https://stackoverflow.com/a/5083666 + File imageFile = new File(FILE_BASE_PATH + fileName); + return Files.readAllBytes(imageFile.toPath()); + } catch(Exception e) { + LOGGER.error(e.getMessage()); + throw new IOException("Opening the file specified failed"); + } + } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java index 8b865e1..00562bb 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java @@ -64,4 +64,12 @@ public interface HorseService { * @throws ValidationException will be thrown if the file is in the incorrect format */ void saveImage(MultipartFile img) throws IOException, ValidationException; + + /** + * @param path of the image to get + * @return the image + * @throws IOException will be thrown if the file could not be accessed + * @throws ValidationException will be thrown if the path does not end with .jpg, .jpeg or .png + */ + byte[] getImage(String path) throws IOException, ValidationException; } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java index d4aa104..6f66ed6 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java @@ -78,4 +78,11 @@ public class SimpleHorseService implements HorseService { LOGGER.trace("saveImage({})", img.getName()); horseFileDao.save(img); } + + @Override + public byte[] getImage(String path) throws IOException, ValidationException { + this.validator.validateImageRequest(path); + LOGGER.trace("getImage({})", path); + return horseFileDao.get(path); + } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java index af9eaf7..1198ed6 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java @@ -68,6 +68,15 @@ public class Validator { } } + public void validateImageRequest(String path) throws ValidationException { + if(path == null || path.isEmpty()) { + throw new ValidationException("No image path supplied"); + } + if(!path.endsWith(".png") && !path.endsWith(".jpg") && !path.endsWith(".jpeg")) { + throw new ValidationException("Unsupported file extension supplied."); + } + } + public void validateHorseFilter(Map filters) throws ValidationException { if(filters.get("score") != null) { try { diff --git a/frontend/wendys-friends/src/app/component/horse/horse.component.html b/frontend/wendys-friends/src/app/component/horse/horse.component.html index 866e867..4546e1a 100644 --- a/frontend/wendys-friends/src/app/component/horse/horse.component.html +++ b/frontend/wendys-friends/src/app/component/horse/horse.component.html @@ -6,12 +6,40 @@
- diff --git a/frontend/wendys-friends/src/app/component/horse/horse.component.ts b/frontend/wendys-friends/src/app/component/horse/horse.component.ts index ed47280..b2f7d4f 100644 --- a/frontend/wendys-friends/src/app/component/horse/horse.component.ts +++ b/frontend/wendys-friends/src/app/component/horse/horse.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { HorseService } from '../../service/horse.service'; import { Horse } from '../../dto/horse'; import { ActivatedRoute } from '@angular/router'; +import { OwnerService } from 'src/app/service/owner.service'; +import { Owner } from 'src/app/dto/owner'; @Component({ selector: 'app-horse', @@ -13,8 +15,9 @@ export class HorseComponent implements OnInit { error = false; errorMessage = ''; horse: Horse; + ownerName: string; - constructor(private horseService: HorseService, private route: ActivatedRoute) { } + constructor(private horseService: HorseService, private route: ActivatedRoute, private ownerService: OwnerService) { } ngOnInit(): void { // Extract id from url @@ -37,6 +40,7 @@ export class HorseComponent implements OnInit { this.horseService.getHorseById(id).subscribe( (horse: Horse) => { this.horse = horse; + this.loadOwnerNameForHorse(horse.owner); }, error => { this.defaultServiceErrorHandling(error); @@ -44,6 +48,23 @@ export class HorseComponent implements OnInit { ); } + /** + * Loads the name of the owner of a horse + * @param id of the owner to get + */ + private loadOwnerNameForHorse(id: number) { + this.ownerService.getOwnerById(id).subscribe( + (owner: Owner) => { + console.log(owner.name) + this.ownerName = owner.name; + console.log(this.ownerName); + }, + error => { + this.ownerName = "N/A"; + } + ) + } + private defaultServiceErrorHandling(error: any) { console.log(error); this.error = true;