Spring Boot Dosya İşleme (Upload / Download)
Birçok uygulama dosya alıp verir: profil fotoğrafı yükleme, CSV/Excel içe aktarma, PDF/rapor indirme, belge yönetimi. Spring Boot, dosya yükleme (multipart) ve indirme işlemlerini basit anotasyonlarla destekler. Bu ko…
Spring Boot Dosya İşleme (Upload / Download)
Birçok uygulama dosya alıp verir: profil fotoğrafı yükleme, CSV/Excel içe aktarma, PDF/rapor indirme, belge yönetimi. Spring Boot, dosya yükleme (multipart) ve indirme işlemlerini basit anotasyonlarla destekler. Bu konu, dosya yükleme ve indirmeyi ve üretimdeki dikkat noktalarını ele alır.
Dosya yükleme: MultipartFile
Tarayıcı/istemci, dosyayı multipart/form-data olarak gönderir; Spring bunu MultipartFile'a
bağlar:
@PostMapping("/yukle") public Map<String,Object> yukle(@RequestParam("dosya") MultipartFile dosya) { dosya.getOriginalFilename(); // istemcideki ad dosya.getSize(); // byte cinsinden boyut dosya.getContentType(); // MIME tipi (image/png ...) byte[] icerik = dosya.getBytes(); // bellekteki içerik // dosya.transferTo(hedefYol); // doğrudan diske yaz ... }
Birden çok dosya: @RequestParam("dosyalar") MultipartFile[] dosyalar veya List<MultipartFile>.
Örnek 1 (./Ornek1.java) bir dosyayı yükler (bellek-içi depo) ve meta bilgisini döndürür.
Dosya indirme: ResponseEntity<byte[]>
İndirme için içeriği ve uygun başlıkları döndürürsün. Content-Disposition: attachment
tarayıcıya "bunu indir" der:
@GetMapping("/indir/{ad}") public ResponseEntity<byte[]> indir(@PathVariable String ad) { return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + ad + "\"") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(icerik); }
Büyük dosyalar için byte[] yerine Resource (InputStreamResource/FileSystemResource)
döndürmek belleği korur (akış halinde gönderir). Örnek 1 yüklenen dosyayı geri indirir ve içeriğin
aynı olduğunu doğrular.
Yapılandırma
application.properties ile yükleme sınırları:
spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=20MB spring.servlet.multipart.enabled=true
Üretim pratikleri ve güvenlik
- Boyut sınırı koy: Aksi halde büyük dosyalar belleği/diski tüketir (DoS riski). Yukarıdaki ayarları kullan.
- Dosya türünü doğrula: Yalnızca uzantıya güvenme; MIME/içerik doğrula. Çalıştırılabilir/ tehlikeli türleri reddet.
- Dosya adını güvenli yap: İstemci adını doğrudan disk yoluna koyma (path traversal:
../../saldırısı). Ad temizle veya rastgele üret. - Bellekte tutma: Büyük dosyaları
getBytes()ile belleğe almak yerinetransferTo/akış kullan veya doğrudan bir nesne deposuna (S3, MinIO) yükle. - Depolama: Üretimde dosyaları uygulama diskinde değil, nesne deposunda (S3/GCS) veya ayrı bir dosya servisinde sakla (ölçeklenme + dayanıklılık).
Özet
Spring Boot'ta dosya işlemeyi öğrendik: multipart/form-data ile yükleme (MultipartFile) ve
Content-Disposition ile indirme (ResponseEntity<byte[]>/Resource; Örnek 1); boyut
sınırı yapılandırması ve güvenlik (tür doğrulama, path traversal, nesne deposu) pratikleri. Sırada,
uygulamayı farklı dillere uyarlamak: uluslararasılaştırma (i18n).
▶ Kod Örnekleri(1)
Ornek1
ortam gerekir1// Ornek1: Dosya yükleme (MultipartFile) ve indirme (ResponseEntity<byte[]>).
2// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
3package com.egitim.springboot.dosya;
4
5import org.springframework.boot.CommandLineRunner;
6import org.springframework.boot.SpringApplication;
7import org.springframework.boot.autoconfigure.SpringBootApplication;
8import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
9import org.springframework.context.annotation.Bean;
10import org.springframework.core.io.ByteArrayResource;
11import org.springframework.http.*;
12import org.springframework.util.LinkedMultiValueMap;
13import org.springframework.util.MultiValueMap;
14import org.springframework.web.bind.annotation.*;
15import org.springframework.web.client.RestClient;
16import org.springframework.web.multipart.MultipartFile;
17
18import java.nio.charset.StandardCharsets;
19import java.util.Map;
20import java.util.concurrent.ConcurrentHashMap;
21
22@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
23public class Ornek1 {
24
25 public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
26
27 @RestController
28 @RequestMapping("/dosya")
29 static class DosyaController {
30 private final Map<String, byte[]> depo = new ConcurrentHashMap<>(); // bellek-içi (gerçekte disk/S3)
31
32 // YÜKLEME: multipart/form-data -> MultipartFile
33 @PostMapping("/yukle")
34 public Map<String, Object> yukle(@RequestParam("dosya") MultipartFile dosya) {
35 depo.put(dosya.getOriginalFilename(), getBytes(dosya));
36 return Map.of("ad", dosya.getOriginalFilename(),
37 "boyut", dosya.getSize(),
38 "tip", String.valueOf(dosya.getContentType()));
39 }
40
41 // İNDİRME: byte[] + Content-Disposition (tarayıcı "indir" diye algılar)
42 @GetMapping("/indir/{ad}")
43 public ResponseEntity<byte[]> indir(@PathVariable String ad) {
44 byte[] icerik = depo.get(ad);
45 if (icerik == null) return ResponseEntity.notFound().build();
46 return ResponseEntity.ok()
47 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + ad + "\"")
48 .contentType(MediaType.APPLICATION_OCTET_STREAM)
49 .body(icerik);
50 }
51
52 private static byte[] getBytes(MultipartFile f) {
53 try { return f.getBytes(); } catch (Exception e) { throw new RuntimeException(e); }
54 }
55 }
56
57 @Bean
58 CommandLineRunner selfTest() {
59 return args -> {
60 RestClient c = RestClient.create("http://localhost:8080");
61 System.out.println("\n========= DOSYA YÜKLE/İNDİR SELF-TEST =========");
62
63 // YÜKLE: multipart gövde oluştur (dosya adı + içerik).
64 byte[] icerik = "Merhaba, bu bir test dosyasıdır.".getBytes(StandardCharsets.UTF_8);
65 MultiValueMap<String, Object> govde = new LinkedMultiValueMap<>();
66 govde.add("dosya", new ByteArrayResource(icerik) {
67 @Override public String getFilename() { return "rapor.txt"; } // dosya adı şart
68 });
69 String yuklemeSonuc = c.post().uri("/dosya/yukle")
70 .contentType(MediaType.MULTIPART_FORM_DATA)
71 .body(govde).retrieve().body(String.class);
72 System.out.println("YÜKLE -> " + yuklemeSonuc);
73
74 // İNDİR: yüklenen dosyayı geri al.
75 byte[] inen = c.get().uri("/dosya/indir/rapor.txt").retrieve().body(byte[].class);
76 System.out.println("İNDİR -> içerik: '" + new String(inen, StandardCharsets.UTF_8) + "'");
77 System.out.println("İçerik aynı mı? " + java.util.Arrays.equals(icerik, inen));
78 System.out.println("===============================================");
79 };
80 }
81}Bu örnek Spring / Spring Boot (Gradle) ortamı gerektirir; tek dosya olarak java Ornek1.java ile çalışmaz. Orijinal portal bunu gömülü Tomcat / Spring context ile koşuyordu. Beklenen davranış yukarıdaki anlatım ve kodda açıklanmıştır.