Wysyłanie plików na serwer przez Spring Boot

Często tworząc aplikacje, potrzebujemy dać możliwość użytkownikom przesyłania plików na serwer (upload). Mogą to być przykładowo zdjęcia przesłane przez użytkowników. W tym wpisie pokażę, jak można to łatwo zrobić.

Zakładam, że wiesz już jak stworzyć podstawową aplikację w Spring Boocie, więc pomijam etap dodawania zależności i tworzenia głównej klasy. Jeśli chcesz się tego nauczyć, możesz przeczytać o tym w poprzednim wpisie: Spring Boot – Szybkie tworzenie aplikacji web w Javie.

Przykładowy kod możecie znaleźć w GitHubie pod adresem: https://github.com/mloza/spring-boot-file-upload

Formularz HTML do wysyłania plików

Na początek będziemy potrzebowali formularza HTML, w którym damy użytkownikowi możliwość wybrania pliku. Tworzymy plik index.html w resources/static i tworzymy w nim formularz.

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>File upload</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="fileupload"/>
<button type="submit">Wyślij</button>
</form>
</body>
</html>
view raw index.html hosted with ❤ by GitHub

Ważne, aby w elemencie form ustawić method=”post” oraz enctype=”multipart/form-data”. Bez tych atrybutów, upload nie zadziała. Action ustawiłem na /upload, pod ten endpoint zostanie przesłany nasz plik.

Po uruchomieniu aplikacji i wejściu na stronę http://localhost:8080 powinniśmy ujrzeć nasz formularz, będzie on wyglądał mniej więcej tak:

Podstawowy formularz

Obsługa wysyłania po stronie serwera

Mając już formularz, musimy obsłużyć odbieranie pliku po stronie serwera. Potrzebujemy controller, który będzie obsługiwał endpoint /upload:

@Controller
public class UploadController {
@PostMapping("/upload")
@ResponseBody // 1
public String handleFile(@RequestPart(name = "fileupload") MultipartFile file) { // 2
File uploadDirectory = new File("uploads");
uploadDirectory.mkdirs(); // 3
try {
File oFile = new File("uploads/" + file.getOriginalFilename());
OutputStream os = new FileOutputStream(oFile);
InputStream inputStream = file.getInputStream();
IOUtils.copy(inputStream, os); // 4
os.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
return "Wystąpił błąd podczas przesyłania pliku: " + e.getMessage();
}
return "ok!";
}
}

To cały kod potrzebny do odebrania pliku i zapisania go na serwerze. W miejscu oznaczonym jako // 1 mówimy, że zwrócimy z metody odpowiedź, normalnie Spring szukałby widoku o nazwie, którą zwracamy. Następnie jako parametr metody przyjmujemy MultipartFile (// 2) o nazwie fileupload (nazwa ta musi być taka sama jak nazwa pola w formularzu. W miejscu // 3 upewniam się, że katalog, do którego chcę zapisać plik, istnieje, a jeśli nie, to go tworzę. Ostatnie co nam pozostało, to stworzyć strumień wyjściowy, pobrać strumień wejściowy z przesłanego pliku i przekopiować zawartość jednego do drugiego (//4). I to wszystko.

Jeżeli uruchomimy teraz aplikację, wybierzemy plik przez formularz i klikniemy przycisk wyślij, powinien się on pojawić w katalogu uploads w głównym katalogu aplikacji. W przeglądarce powinniśmy otrzymać napis „ok!”.

Wyświetlanie wysłanych plików

Mamy już wysyłanie na serwer, teraz chcielibyśmy pobrać te pliki. Załóżmy, że obsługiwaliśmy wysyłanie obrazków. Aby je wyświetlić, musimy ustawić odpowiednio nagłówek content-type i zwrócić zawartość obrazka jako body. Umożliwi nam to poniższy kod.

@GetMapping("image/{name}")
public ResponseEntity showImage(@PathVariable String name) throws IOException {
File file = new File("uploads/" + name);
if (!file.exists()) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok()
.contentType(MediaType.valueOf(URLConnection.guessContentTypeFromName(name)))
.body(Files.readAllBytes(file.toPath()));
}

Po wejściu pod adres http://localhost:8080/image/nazwa-obrazka.jpg zostanie wyświetlony wysłany wcześniej obraz. Aby zwrócić odpowiedni content-type, jest on tworzony (jak nazwa metody mówi, zgadywany) na podstawie nazwy pliku.

Podsumowanie

Prosty upload plików możemy stworzyć bardzo szybko przy pomocy niewielkiej ilości kodu. Jednak przed zapisaniem pliku na dysku należałoby go zweryfikować, czy rzeczywiście przesłany plik to obrazek, czy może ktoś próbuje wrzucić nam na serwer jakieś niepożądane pliki. Tak samo powinniśmy się upewnić, że plik, który jest zwracany to plik, który chcemy pokazać. Jest to jednak temat na oddzielny post. 

4 myśli na “Wysyłanie plików na serwer przez Spring Boot”

    1. Aktualnie nie mam tego w repo. Jeśli napiszesz jaki masz problem to postaram się pomóc, lub możesz podesłać link do repo gdzie to nie działa to zerknę.

    1. Możesz użyć przykładowo takiego kodu:

      @GetMapping("image/{id}")
      public ResponseEntity image(@PathParam('id') String id) throws IOException {
          File file = new File("uploads/"+ id);
      
          return ResponseEntity.ok()
                  .header("Content-Disposition", "attachment; filename=" +file.getName())
                  .contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(file)))
                  .body(Files.readAllBytes(file.toPath()));
      }
      

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *