Only POST is working. GET and DELETE are throwing the following error when testing through Postman:
{
"timestamp": "2023-04-06T14:01:59.898+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/secure/test/file/bull.png"
}
I haven't worked with Multipart files before and I have a feeling that it's something regarding that that goes wrong, but I can't quite put my finger on what exactly. I'll put the relevant pieces of code in the following code blocks.
package server.api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import server.services.StorageService;
import java.io.IOException;
@SpringBootApplication
@RestController
@RequestMapping(value = {"/secure/{username}/{password}/file", "/secure/{username}/file"})
public class FileController {
@Autowired
private StorageService service;
@PostMapping("/add")
public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
String uploadFile = service.uploadFile(file);
return ResponseEntity.status(HttpStatus.OK)
.body(uploadFile);
}
@GetMapping("/{fileName}")
public ResponseEntity<?> outputFile(@PathVariable String fileName){
byte[] fileData=service.outputFile(fileName);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(fileData);
}
@DeleteMapping("/{fileName}")
public ResponseEntity<?> deleteFile(@PathVariable String fileName) {
String message = service.deleteFile(fileName);
return ResponseEntity.status(HttpStatus.OK)
.body(message);
}
}
package server.services;
import commons.FileData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import server.database.StorageRepository;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Optional;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
@Service
public class StorageService {
@Autowired
private StorageRepository repository;
public String uploadFile(MultipartFile file) throws IOException {
repository.save(new FileData(file.getOriginalFilename(),
file.getContentType(), compressFile(file.getBytes())));
return "File uploaded succesfully: "+ file.getOriginalFilename();
}
public byte[] outputFile(String fileName){
Optional<FileData> dbFileData = repository.findByName(fileName);
byte[] file=decompressFile(dbFileData.get().getFileData());
return file;
}
public String deleteFile(String fileName) {
Optional<FileData> dbFileData = repository.findByName(fileName);
if (dbFileData.isPresent()) {
repository.delete(dbFileData.get());
return "File deleted successfully: " + fileName;
} else {
return "File not found: " + fileName;
}
}
public String getType(String fileName){
Optional<FileData> dbFileData = repository.findByName(fileName);
if (dbFileData.isPresent()) {
return dbFileData.get().getType();
} else {
return "File not found: " + fileName;
}
}
public static byte[] compressFile(byte[] data) {
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.BEST_COMPRESSION);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4*1024];
while (!deflater.finished()) {
int size = deflater.deflate(tmp);
outputStream.write(tmp, 0, size);
}
try {
outputStream.close();
} catch (Exception ignored) {
}
return outputStream.toByteArray();
}
public static byte[] decompressFile(byte[] data) {
Inflater inflater = new Inflater();
inflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4*1024];
try {
while (!inflater.finished()) {
int count = inflater.inflate(tmp);
outputStream.write(tmp, 0, count);
}
outputStream.close();
} catch (Exception ignored) {
}
return outputStream.toByteArray();
}
}
package commons;
import lombok.Data;
import javax.persistence.*;
@Entity
@Table(name="FileData")
@Data
public class FileData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
@Lob
@Column(name="filedata")
private byte[] fileData;
public FileData(String name, String type, byte[] fileData) {
this.name = name;
this.type = type;
this.fileData = fileData;
}
public byte[] getFileData() {
return fileData;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
public Long getId() {
return id;
}
}
The problem was the missing default constructor of the FileData
class.
Even though I had one with parameters, that's not considered default as the default constructor has to be without parameters. Adding this line to the FileData
class solved the issue
public FileData() {}