Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sma-97-create-backend-for-storing-images #84

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: upload image
kz44 committed Apr 23, 2024
commit aa9c34620da523f33b6cd4a67404a73f582cefd2
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package com.sportsmatch.controllers;

import com.sportsmatch.dtos.UserInfoDTO;
import com.sportsmatch.models.Image;
import com.sportsmatch.services.UserService;
import com.sportsmatch.services.ValidationService;
import com.sportsmatch.util.ImageUtils;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/user")
@@ -40,4 +49,25 @@ public ResponseEntity<?> updateInfo(
return ResponseEntity.status(e.getStatusCode()).build();
}
}

@PostMapping("/image")
public ResponseEntity<Long> uploadProfileImage(@RequestParam("image") MultipartFile file) throws IOException {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.uploadProfileImage(file));
}

@DeleteMapping("/image/{id}")
public void deleteProfileImage(@PathVariable Long id) {
userService.deleteProfileImage(id);
}

@GetMapping("/image/{id}")
public ResponseEntity<ByteArrayResource> downloadProfileImage(@PathVariable Long id) {
Image image = userService.downloadProfileImage(id);

return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(image.getType()))
.header(HttpHeaders.CONTENT_DISPOSITION,
"image; filename=\"" + image.getName() + "\"")
.body(new ByteArrayResource(ImageUtils.decompressImage(image.getImageData())));
}
}
Original file line number Diff line number Diff line change
@@ -15,4 +15,5 @@ public class UserDTO {
private Integer win;
private Integer loss;
private List<SportDTO> sports;
private Integer imageId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sportsmatch.models;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "images")
public class Image {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;

private String type;

@Lob
@Column(length = 5000000)
private byte[] imageData;
}
Original file line number Diff line number Diff line change
@@ -43,6 +43,8 @@ public class User implements UserDetails {

private Integer totalPlayed = 0;

private Long imageId;

@Enumerated(EnumType.STRING)
private Role role;

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sportsmatch.repositories;

import com.sportsmatch.models.Image;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ImageRepository extends JpaRepository <Image, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.sportsmatch.services;

import com.sportsmatch.models.Image;
import com.sportsmatch.repositories.ImageRepository;
import com.sportsmatch.util.ImageUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.NoSuchElementException;

@Service
@RequiredArgsConstructor
public class ImageService {

private final ImageRepository imageRepository;

/**
* Upload an image file to the system.
*
* @param file image file to upload.
* @return ID of the uploaded image.
* @throws IOException if an I/O error occurs while reading the file
*/
public Long uploadImage(MultipartFile file) throws IOException {
Image newImage = Image.builder()
.name(file.getOriginalFilename())
.type(file.getContentType())
.imageData(ImageUtils.compressImage(file.getBytes())).build();

imageRepository.save(newImage);
return newImage.getId();
}

/**
* Download an image.
*
* @param id ID of the image to download.
* @return the downloaded Image object.
* @throws NoSuchElementException if no image with the given ID exists.
*/
public Image downloadImage(Long id) {
return imageRepository.findById(id).orElseThrow();
}

/**
* Deletes an image.
*
* @param id ID of the image to delete.
*/
public void deleteImage(Long id) {
imageRepository.deleteById(id);
}
}
Original file line number Diff line number Diff line change
@@ -2,7 +2,11 @@

import com.sportsmatch.dtos.UserDTO;
import com.sportsmatch.dtos.UserInfoDTO;
import com.sportsmatch.models.Image;
import com.sportsmatch.models.User;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

public interface UserService {

@@ -15,4 +19,11 @@ public interface UserService {
UserDTO getUserById(Long id);

UserDTO getMyRank();

Long uploadProfileImage(MultipartFile file) throws IOException;

void deleteProfileImage(Long id);

Image downloadProfileImage(Long id);

}
Original file line number Diff line number Diff line change
@@ -15,8 +15,10 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
@@ -30,6 +32,7 @@ public class UserServiceImp implements UserService {
private final SportMapper sportMapper;
private final SportRepository sportRepository;
private final UserMapper userMapper;
private final ImageService imageService;
private final RankService rankService;

/**
@@ -98,6 +101,43 @@ public void updateUserInfo(UserInfoDTO userInfoDTO) {
userRepository.save(user);
}


/**
* Uploads a profile image for logged-in user.
*
* @param image the profile image file to upload.
* @return ID of the uploaded profile image.
* @throws IOException if an I/O error occurs while reading the image file.
*/
public Long uploadProfileImage(MultipartFile image) throws IOException {
User loggedUser = getUserFromContext();
loggedUser.setImageId(imageService.uploadImage(image));
return loggedUser.getImageId();
}

/**
* Delete a profile image for logged-in user.
*
* @param id ID of the uploaded profile image.
*/
@Override
public void deleteProfileImage(Long id) {
User loggedUser = getUserFromContext();
loggedUser.setImageId(null);
imageService.deleteImage(id);
}

/**
* Download a profile image by the given ID for logged-in user.
*
* @param id ID of the image that will be downloaded.
* @return an image by the given id.
*/
@Override
public Image downloadProfileImage(Long id) {
return imageService.downloadImage(id);
}

private void parseUserDateOfBirth(UserInfoDTO userInfoDTO, User user) {
try {
user.setDateOfBirth(
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.sportsmatch.util;

import java.io.ByteArrayOutputStream;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

public class ImageUtils {

/**
* Compresses the given image data using the DEFLATE algorithm.
*
* @param data The image data to compress.
* @return The compressed image data.
*/
public static byte[] compressImage(byte[] data) {

// Creating deflater object
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.BEST_COMPRESSION); // setting compression level to the best compression
deflater.setInput(data); // setting input data for compression
deflater.finish(); // finishing compression

// Creating output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4 * 1024]; // creating temporary buffer
while (!deflater.finished()) {
int size = deflater.deflate(tmp); // Compressing data and writing it to the output stream
outputStream.write(tmp, 0, size);
}
try {
outputStream.close(); // Closing the output stream
} catch (Exception expection) { // Handling in case of error
}
return outputStream.toByteArray(); // Returning compressed data as byte array
}


/**
* Decompresses the given compressed image data using the DEFLATE algorithm.
*
* @param data The compressed image data to decompress.
* @return The decompressed image data.
*/
public static byte[] decompressImage(byte[] data) {

// Creating inflater object
Inflater inflater = new Inflater();
inflater.setInput(data); // Setting input data for decompression

// Creating output stream
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4 * 1024]; // Creating temporary buffer
try {
while (!inflater.finished()) {
int count = inflater.inflate(tmp); // Decompressing compressed data and writing it to the output stream
outputStream.write(tmp, 0, count);
}
outputStream.close(); // Closing the output stream
} catch (Exception expection) { // Handling in case of error
}
// Returning decompressed data as byte array
return outputStream.toByteArray();
}
}