0

Using Spring Boot, I am trying to implement a REST controller, which can handle a GET request asking to return a BLOB object from my database.

Googling around a little bit, and putting pieces together, I have created the following code snippet:

@GetMapping("student/pic/studentId")
public void getProfilePicture(@PathVariable Long studentId, HttpServletResponse response) throws IOException {
    Optional<ProfilePicture> profilePicture;
    profilePicture = profilePictureService.getProfilePictureByStudentId(studentId);
    if (profilePicture.isPresent()) {
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(profilePicture.get().getPicture());
        outputStream.close();
    }
}

I am sending the GET request using VanillaJS and the fetch-API:

async function downloadPicture(profilePic, studentId) {
  const url = "http://localhost:8080/student/pic/" + studentId;
  const response = await fetch(url);
  const responseBlob = await response.blob();
  if (responseBlob.size > 0) {
    profilePic.src = URL.createObjectURL(responseBlob);
  } 
}

Somehow, this works. That's great, but now I would like to understand the usage of HttpServletResponse in this context, which I am not familiar with. It seems to me that the fetch-API makes use of HttpServletResponse (maybe even creates it), since I am not creating this object or do anything with it.

What is very strange to me is that the return-type of my controller method getProfilePicture() is void, and still I am sending a response, which is most definitely not void.

Also, if the profilePicture was not found in my database, for example due to a non-existing studentId being passed, my controller-method does not do anything. But still, I am getting a response code of 200. That's why I have added the responseBlob.size > 0 part in my Javascript to check for a positive response.

Can someone explain this magic to me, please?

Luk
  • 1,009
  • 2
  • 15
  • 33

1 Answers1

1

response.getOutputStream(); javadoc says "Returns a ServletOutputStream suitable for writing binary data in the response." It's literally the response stream and you write the picture bytes into it. It's not related to the client reading the response. Alternatively you could just return a byte array which will be automatically written into the response stream and the result will be the same.

To return a different http status code you should change the method return type to ResponseEntity<byte[]>:

@GetMapping("student/pic/studentId")
public ResponseEntity<byte[]> getProfilePicture(@PathVariable Long studentId, HttpServletResponse response) throws IOException {
    Optional<ProfilePicture> profilePicture = profilePictureService.getProfilePictureByStudentId(studentId);
    if (profilePicture.isPresent()) {
        return ResponseEntity.ok(profilePicture.get().getPicture()); //status code 200
    } else {
        return ResponseEntity.notFound().build(); //status code 404
    }
}

ResponseEntity is basically springs way to return different status codes/messages.

Is there a reason why you are manually downloading the image via javascript? You could just create a img element with the http link to the image and the browser will automatically display the image content: <img src="http://localhost:8080/student/pic/studentId">

Cyril
  • 2,376
  • 16
  • 21
  • Thank you so much for this answer, it is loaded with so much valuable information that is new to me! I will try out to show the image directly by placing the http-link as src into my img tag. Can I ask you two follow-up questions first, though? 1.: this `HttpServletResponse` parameter, is Spring Boot placing it into my `getProfilePicture()` method? Because I don't provide it, contrary to the studentId, which is a part of my URL. 2.: If the provided studentId is not found in my database, I return a 404 now. This is displayed in my Browser console. Can I prevent this from showing up? – Luk Jan 04 '22 at 14:24
  • 1. Yes. Spring will automatically inject it if you declare it as a method parameter. Also works with HttpServletRequest. Most of the time you don't need it because Spring provides a way to handle almost anything without accessing the servlet requests/responses directly. 2. Short answer no: https://stackoverflow.com/questions/7035466/check-if-file-exists-but-prevent-404-error-in-console-from-showing-up – Cyril Jan 04 '22 at 14:52
  • ok. Well, I am loading a default picture, if no picture can be found for the specified studentId. I could return this, instead of throwing the 404. But I think I will just throw the 404 instead and afterwards make a GET request for the default image. Is this ok, or would you not recommend it? – Luk Jan 04 '22 at 15:19
  • 1
    I would probably return a default picture instead of 404. You could also add a "picture exists" flag to the student response and only request the picture if the student has one. – Cyril Jan 04 '22 at 15:37