{"id":31627,"date":"2025-10-12T02:41:01","date_gmt":"2025-10-11T23:41:01","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/?p=31627"},"modified":"2025-10-12T02:41:01","modified_gmt":"2025-10-11T23:41:01","slug":"java-ile-urlden-dosya-indirme-kapsamli-bir-rehber","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/","title":{"rendered":"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber"},"content":{"rendered":"<p><body><\/p>\n<h2>Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber<\/h2>\n<h2>Giri\u015f<\/h2>\n<p>G\u00fcn\u00fcm\u00fcz\u00fcn birbirine ba\u011fl\u0131 d\u00fcnyas\u0131nda, uygulamalar\u0131n internet \u00fczerinden veri almas\u0131 temel bir gereklilik haline gelmi\u015ftir. Bir web sayfas\u0131n\u0131n i\u00e7eri\u011fini okumaktan, bir API&#8217;den JSON verisi \u00e7ekmeye veya uzak bir sunucudan b\u00fcy\u00fck bir dosyay\u0131 indirmeye kadar, Java&#8217;n\u0131n a\u011f yetenekleri bu g\u00f6revleri yerine getirmek i\u00e7in g\u00fc\u00e7l\u00fc ara\u00e7lar sunar. Bu makale, Java kullanarak bir URL&#8217;den dosya indirme konusunu derinlemesine inceleyecek, basit y\u00f6ntemlerden ba\u015flayarak geli\u015fmi\u015f senaryolara, hata y\u00f6netimine ve en iyi uygulamalara kadar geni\u015f bir yelpazeyi kapsayacakt\u0131r. \u0130ster bir web uygulamas\u0131nda kullan\u0131c\u0131lar\u0131n dosya indirmesini sa\u011flamak, ister bir arka plan hizmetinde veri setlerini senkronize etmek veya otomasyon betikleri yazmak isteyin, burada sunulan bilgiler size sa\u011flam bir temel sa\u011flayacakt\u0131r. Amac\u0131m\u0131z, Java&#8217;n\u0131n farkl\u0131 s\u00fcr\u00fcmlerinde ve farkl\u0131 ihtiya\u00e7lara y\u00f6nelik olarak sunulan \u00e7e\u015fitli yakla\u015f\u0131mlar\u0131 detayland\u0131rmak ve her birinin avantajlar\u0131 ile dezavantajlar\u0131n\u0131 ortaya koymakt\u0131r.<\/p>\n<h2>Temel Dosya \u0130ndirme Y\u00f6ntemleri<\/h2>\n<p>Java, uzak bir kaynaktan dosya indirmek i\u00e7in \u00e7e\u015fitli API&#8217;ler sunar. Bu b\u00f6l\u00fcmde, en yayg\u0131n ve temel yakla\u015f\u0131mlar\u0131 ele alaca\u011f\u0131z.<\/p>\n<h3>java.net.URL ve java.io.InputStream Kullan\u0131m\u0131<\/h3>\n<p>Bu y\u00f6ntem, Java&#8217;n\u0131n ilk zamanlar\u0131ndan beri var olan ve hala bir\u00e7ok basit indirme senaryosu i\u00e7in ge\u00e7erli olan klasik bir yakla\u015f\u0131md\u0131r. <code>java.net.URL<\/code> s\u0131n\u0131f\u0131, bir URL&#8217;yi temsil eder ve bu URL \u00fczerinden bir ba\u011flant\u0131 kurmay\u0131 sa\u011flar. Kurulan ba\u011flant\u0131dan bir <code>InputStream<\/code> elde edilerek, uzak sunucudan gelen veriler okunabilir ve yerel bir <code>FileOutputStream<\/code> arac\u0131l\u0131\u011f\u0131yla dosyaya yaz\u0131labilir.<\/p>\n<p>Bu i\u015flemin ad\u0131mlar\u0131 genellikle \u015funlard\u0131r:<br \/>\n1.  <strong>URL Olu\u015fturma:<\/strong> \u0130ndirilecek dosyan\u0131n URL&#8217;sini i\u00e7eren bir <code>URL<\/code> nesnesi olu\u015fturulur.<br \/>\n2.  <strong>Ba\u011flant\u0131 A\u00e7ma:<\/strong> <code>URL.openConnection()<\/code> metodu ile bir <code>URLConnection<\/code> nesnesi elde edilir. Bu nesne, a\u011f ba\u011flant\u0131s\u0131 \u00fczerinde daha fazla kontrol (timeout ayarlar\u0131, ba\u015fl\u0131k ekleme vb.) sa\u011flar.<br \/>\n3.  <strong>Giri\u015f Ak\u0131\u015f\u0131 Alma:<\/strong> <code>URLConnection.getInputStream()<\/code> metodu \u00e7a\u011fr\u0131larak uzak kaynaktan veri okumak i\u00e7in bir <code>InputStream<\/code> elde edilir.<br \/>\n4.  <strong>\u00c7\u0131k\u0131\u015f Ak\u0131\u015f\u0131 Olu\u015fturma:<\/strong> \u0130ndirilen verinin kaydedilece\u011fi yerel dosya i\u00e7in bir <code>FileOutputStream<\/code> olu\u015fturulur.<br \/>\n5.  <strong>Veri Kopyalama:<\/strong> <code>InputStream<\/code>&#8216;den okunan veriler, bir tampon (buffer) kullan\u0131larak <code>FileOutputStream<\/code>&#8216;a yaz\u0131l\u0131r.<br \/>\n6.  <strong>Kaynaklar\u0131 Kapatma:<\/strong> \u0130\u015flem tamamland\u0131\u011f\u0131nda veya bir hata olu\u015ftu\u011funda, a\u00e7\u0131k olan t\u00fcm ak\u0131\u015flar (InputStream ve FileOutputStream) kapat\u0131lmal\u0131d\u0131r. <code>try-with-resources<\/code> yap\u0131s\u0131 bu i\u015flemi g\u00fcvenli ve otomatik hale getirir.<\/p>\n<pre><code class=\"language-java\">import java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\npublic class BasicDownloader {\n\n    public static void downloadFile(String fileURL, String saveDir, String fileName) throws IOException {\n        URL url = new URL(fileURL);\n        URLConnection connection = url.openConnection();\n        \n        \/\/ Ba\u011flant\u0131 ve okuma zaman a\u015f\u0131m\u0131 ayarlar\u0131\n        connection.setConnectTimeout(5000); \/\/ 5 saniye ba\u011flant\u0131 zaman a\u015f\u0131m\u0131\n        connection.setReadTimeout(10000);   \/\/ 10 saniye okuma zaman a\u015f\u0131m\u0131\n\n        \/\/ \u0130ndirilecek dosyan\u0131n boyutunu al (varsa)\n        int fileSize = connection.getContentLength();\n        System.out.println(\"Dosya boyutu: \" + (fileSize > 0 ? fileSize + \" byte\" : \"Bilinmiyor\"));\n\n        Path savePath = Paths.get(saveDir, fileName);\n\n        try (InputStream in = connection.getInputStream();\n             FileOutputStream out = new FileOutputStream(savePath.toFile())) {\n\n            byte[] buffer = new byte[4096]; \/\/ 4 KB'l\u0131k bir tampon\n            int bytesRead;\n            long totalBytesRead = 0;\n\n            System.out.println(\"Dosya indirilmeye ba\u015flan\u0131yor: \" + fileURL);\n\n            while ((bytesRead = in.read(buffer)) != -1) {\n                out.write(buffer, 0, bytesRead);\n                totalBytesRead += bytesRead;\n                \/\/ \u0130lerleme takibi eklenebilir\n            }\n            System.out.println(\"Dosya ba\u015far\u0131yla indirildi: \" + savePath.toAbsolutePath());\n\n        } catch (IOException e) {\n            System.err.println(\"Dosya indirilirken bir hata olu\u015ftu: \" + e.getMessage());\n            throw e; \/\/ Hatay\u0131 yeniden f\u0131rlat\n        }\n    }\n\n    public static void main(String[] args) {\n        String fileURL = \"https:\/\/example.com\/somefile.zip\"; \/\/ Ger\u00e7ek bir URL ile de\u011fi\u015ftirin\n        String saveDir = \".\/downloads\"; \/\/ Kaydedilecek dizin\n        String fileName = \"downloaded_file.zip\"; \/\/ Kaydedilecek dosya ad\u0131\n\n        \/\/ Dizin yoksa olu\u015ftur\n        Path dirPath = Paths.get(saveDir);\n        if (!dirPath.toFile().exists()) {\n            dirPath.toFile().mkdirs();\n        }\n\n        try {\n            downloadFile(fileURL, saveDir, fileName);\n        } catch (IOException e) {\n            System.err.println(\"\u0130ndirme i\u015flemi ba\u015far\u0131s\u0131z oldu.\");\n        }\n    }\n}<\/code><\/pre>\n<p>Bu y\u00f6ntem basitlik sunsa da, baz\u0131 dezavantajlar\u0131 vard\u0131r: temel olarak senkron (blocking) bir i\u015flemdir, yani indirme bitene kadar \u00e7a\u011fr\u0131 yapan i\u015f par\u00e7ac\u0131\u011f\u0131 bekler. Ayr\u0131ca, HTTP \u00f6zel ba\u015fl\u0131klar\u0131n\u0131 veya daha karma\u015f\u0131k a\u011f senaryolar\u0131n\u0131 y\u00f6netmek i\u00e7in <code>URLConnection<\/code> \u00fczerinde manuel ayarlamalar gerektirebilir.<\/p>\n<h3>java.nio.channels.Channels ve ReadableByteChannel Kullan\u0131m\u0131<\/h3>\n<p>Java NIO (New Input\/Output) API&#8217;si, geleneksel I\/O&#8217;ya g\u00f6re daha y\u00fcksek performansl\u0131 ve esnek bir yakla\u015f\u0131m sunar. <code>java.nio.channels.Channels<\/code> s\u0131n\u0131f\u0131, bir <code>InputStream<\/code>&#8216;i bir <code>ReadableByteChannel<\/code>&#8216;a d\u00f6n\u00fc\u015ft\u00fcrmek i\u00e7in kullan\u0131labilir. Daha sonra, bu kanal ile bir <code>FileOutputStream<\/code>&#8216;un kanal\u0131n\u0131 (<code>FileChannel<\/code>) kullanarak do\u011frudan veri aktar\u0131m\u0131 yap\u0131labilir. <code>FileChannel.transferFrom()<\/code> metodu, i\u015fletim sistemi seviyesinde do\u011frudan veri aktar\u0131m\u0131 yaparak, kullan\u0131c\u0131 alan\u0131 ile \u00e7ekirdek alan\u0131 aras\u0131ndaki veri kopyalama maliyetini azaltabilir. Bu, \u00f6zellikle b\u00fcy\u00fck dosyalar i\u00e7in performans avantaj\u0131 sa\u011flayabilir.<\/p>\n<pre><code class=\"language-java\">import java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.nio.channels.Channels;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\npublic class NIOFileDownloader {\n\n    public static void downloadFile(String fileURL, String saveDir, String fileName) throws IOException {\n        URL url = new URL(fileURL);\n        Path savePath = Paths.get(saveDir, fileName);\n\n        try (ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());\n             FileOutputStream fileOutputStream = new FileOutputStream(savePath.toFile())) {\n\n            System.out.println(\"Dosya indirilmeye ba\u015flan\u0131yor (NIO): \" + fileURL);\n            fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);\n            System.out.println(\"Dosya ba\u015far\u0131yla indirildi (NIO): \" + savePath.toAbsolutePath());\n\n        } catch (IOException e) {\n            System.err.println(\"NIO ile dosya indirilirken bir hata olu\u015ftu: \" + e.getMessage());\n            throw e;\n        }\n    }\n\n    public static void main(String[] args) {\n        String fileURL = \"https:\/\/example.com\/another_file.pdf\"; \/\/ Ger\u00e7ek bir URL ile de\u011fi\u015ftirin\n        String saveDir = \".\/downloads\";\n        String fileName = \"downloaded_nio_file.pdf\";\n\n        Path dirPath = Paths.get(saveDir);\n        if (!dirPath.toFile().exists()) {\n            dirPath.toFile().mkdirs();\n        }\n\n        try {\n            downloadFile(fileURL, saveDir, fileName);\n        } catch (IOException e) {\n            System.err.println(\"NIO indirme i\u015flemi ba\u015far\u0131s\u0131z oldu.\");\n        }\n    }\n}<\/code><\/pre>\n<p>Bu y\u00f6ntem, <code>InputStream<\/code> tabanl\u0131 yakla\u015f\u0131ma g\u00f6re daha az kodla daha y\u00fcksek performans potansiyeli sunar. Ancak, <code>transferFrom<\/code> metodu da temelde blocking bir i\u015flemdir.<\/p>\n<h3>java.net.http.HttpClient Kullan\u0131m\u0131 (Java 11+)<\/h3>\n<p>Java 11 ile birlikte gelen <code>java.net.http.HttpClient<\/code>, modern HTTP ileti\u015fimleri i\u00e7in tasarlanm\u0131\u015f g\u00fc\u00e7l\u00fc, esnek ve asenkron yeteneklere sahip yeni bir API&#8217;dir. Bu API, HTTP\/2 deste\u011fi, WebSocket&#8217;ler, daha iyi performans ve daha temiz bir kod yap\u0131s\u0131 sunar. Dosya indirme i\u015flemleri i\u00e7in <code>HttpClient<\/code> kullanmak, \u00f6zellikle karma\u015f\u0131k HTTP senaryolar\u0131nda (kimlik do\u011frulama, \u00f6zel ba\u015fl\u0131klar, yeniden y\u00f6nlendirmeler vb.) b\u00fcy\u00fck avantajlar sa\u011flar.<\/p>\n<p><code>HttpClient<\/code> ile dosya indirme ad\u0131mlar\u0131:<br \/>\n1.  <strong><code>HttpClient<\/code> Olu\u015fturma:<\/strong> Bir <code>HttpClient<\/code> \u00f6rne\u011fi olu\u015fturulur. Bu istemci, ba\u011flant\u0131 havuzlama, proxy ayarlar\u0131 ve SSL ba\u011flam\u0131 gibi global ayarlar\u0131 y\u00f6netebilir.<br \/>\n2.  <strong><code>HttpRequest<\/code> Olu\u015fturma:<\/strong> \u0130ndirilecek kayna\u011f\u0131n URL&#8217;sini ve gerekli HTTP metodunu (genellikle GET) i\u00e7eren bir <code>HttpRequest<\/code> nesnesi olu\u015fturulur. \u0130ste\u011fe ba\u011fl\u0131 olarak ba\u015fl\u0131klar, zaman a\u015f\u0131m\u0131 ayarlar\u0131 gibi ek bilgiler eklenebilir.<br \/>\n3.  <strong>\u0130ste\u011fi G\u00f6nderme ve Yan\u0131t\u0131 \u0130\u015fleme:<\/strong> <code>HttpClient.send()<\/code> veya <code>HttpClient.sendAsync()<\/code> metotlar\u0131 kullan\u0131larak istek g\u00f6nderilir. <code>HttpResponse.BodyHandlers<\/code> s\u0131n\u0131f\u0131, yan\u0131t g\u00f6vdesini \u00e7e\u015fitli formatlarda (dize, bayt dizisi, dosya vb.) i\u015flemek i\u00e7in haz\u0131r i\u015fleyiciler sunar. Dosya indirme i\u00e7in <code>HttpResponse.BodyHandlers.ofFile()<\/code> en uygun se\u00e7enektir.<\/p>\n<pre><code class=\"language-java\">import java.io.IOException;\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.Duration;\n\npublic class HttpClientDownloader {\n\n    public static void downloadFile(String fileURL, String saveDir, String fileName) throws IOException, InterruptedException {\n        HttpClient client = HttpClient.newBuilder()\n                .version(HttpClient.Version.HTTP_2) \/\/ HTTP\/2 kullan\n                .connectTimeout(Duration.ofSeconds(5)) \/\/ Ba\u011flant\u0131 zaman a\u015f\u0131m\u0131\n                .build();\n\n        Path savePath = Paths.get(saveDir, fileName);\n\n        HttpRequest request = HttpRequest.newBuilder()\n                .uri(URI.create(fileURL))\n                .GET() \/\/ GET iste\u011fi\n                .timeout(Duration.ofSeconds(20)) \/\/ \u0130stek zaman a\u015f\u0131m\u0131\n                .build();\n\n        System.out.println(\"Dosya indirilmeye ba\u015flan\u0131yor (HttpClient): \" + fileURL);\n\n        try {\n            HttpResponse<Path> response = client.send(request, HttpResponse.BodyHandlers.ofFile(savePath));\n\n            if (response.statusCode() == 200) {\n                System.out.println(\"Dosya ba\u015far\u0131yla indirildi: \" + response.body().toAbsolutePath());\n                \/\/ \u0130ndirilen dosyan\u0131n boyutu gibi ek bilgiler yan\u0131t ba\u015fl\u0131klar\u0131ndan al\u0131nabilir\n                response.headers().firstValue(\"Content-Length\").ifPresent(length -> \n                    System.out.println(\"\u0130ndirilen dosya boyutu: \" + length + \" byte\"));\n            } else {\n                System.err.println(\"Dosya indirilemedi. HTTP Durum Kodu: \" + response.statusCode());\n                System.err.println(\"Yan\u0131t g\u00f6vdesi (ilk 200 karakter): \" + response.body().toString().substring(0, Math.min(response.body().toString().length(), 200)));\n                throw new IOException(\"HTTP \u0130ndirme Hatas\u0131: \" + response.statusCode());\n            }\n        } catch (IOException | InterruptedException e) {\n            System.err.println(\"HttpClient ile dosya indirilirken bir hata olu\u015ftu: \" + e.getMessage());\n            throw e;\n        }\n    }\n\n    public static void main(String[] args) {\n        String fileURL = \"https:\/\/jsonplaceholder.typicode.com\/posts\/1\"; \/\/ K\u00fc\u00e7\u00fck bir JSON dosyas\u0131 i\u00e7in \u00f6rnek URL\n        String saveDir = \".\/downloads\";\n        String fileName = \"downloaded_httpclient_data.json\";\n\n        Path dirPath = Paths.get(saveDir);\n        if (!dirPath.toFile().exists()) {\n            dirPath.toFile().mkdirs();\n        }\n\n        try {\n            downloadFile(fileURL, saveDir, fileName);\n        } catch (IOException | InterruptedException e) {\n            System.err.println(\"HttpClient indirme i\u015flemi ba\u015far\u0131s\u0131z oldu.\");\n        }\n    }\n}<\/code><\/pre>\n<p><code>HttpClient<\/code>, asenkron indirme yetenekleri (sendAsync) sayesinde UI uygulamalar\u0131nda veya y\u00fcksek performansl\u0131 sunucu uygulamalar\u0131nda donmay\u0131 \u00f6nleyebilir. Ayr\u0131ca, otomatik yeniden y\u00f6nlendirme y\u00f6netimi ve proxy deste\u011fi gibi bir\u00e7ok geli\u015fmi\u015f \u00f6zelli\u011fi de kolayca yap\u0131land\u0131rma imkan\u0131 sunar.<\/p>\n<h2>Geli\u015fmi\u015f \u0130ndirme Senaryolar\u0131<\/h2>\n<p>Basit dosya indirme ihtiya\u00e7lar\u0131n\u0131n \u00f6tesinde, ger\u00e7ek d\u00fcnya uygulamalar\u0131nda daha karma\u015f\u0131k senaryolarla kar\u015f\u0131la\u015f\u0131labilir. Bu b\u00f6l\u00fcmde, bu geli\u015fmi\u015f durumlar\u0131 ve Java&#8217;daki \u00e7\u00f6z\u00fcmlerini ele alaca\u011f\u0131z.<\/p>\n<h3>Progress Takibi (\u0130lerleme G\u00f6stergesi)<\/h3>\n<p>\u00d6zellikle b\u00fcy\u00fck dosyalar indirilirken, kullan\u0131c\u0131lara veya sistem y\u00f6neticilerine indirme i\u015fleminin durumu hakk\u0131nda bilgi vermek \u00f6nemlidir. Bu, indirilen bayt say\u0131s\u0131n\u0131 toplam dosya boyutuna oranlayarak bir y\u00fczde ilerlemesi \u015feklinde g\u00f6sterilebilir.<\/p>\n<p><code>InputStream<\/code> tabanl\u0131 indirmelerde, <code>read()<\/code> metodunun her \u00e7a\u011fr\u0131s\u0131nda okunan bayt say\u0131s\u0131n\u0131 toplayarak ilerleme takibi yap\u0131labilir.<\/p>\n<pre><code class=\"language-java\">\/\/ InputStream tabanl\u0131 \u00f6rnekten devamla\nlong totalBytesRead = 0;\nint fileSize = connection.getContentLength(); \/\/ E\u011fer Content-Length ba\u015fl\u0131\u011f\u0131 varsa\n\nwhile ((bytesRead = in.read(buffer)) != -1) {\n    out.write(buffer, 0, bytesRead);\n    totalBytesRead += bytesRead;\n    if (fileSize > 0) {\n        int progress = (int) ((totalBytesRead * 100) \/ fileSize);\n        \/\/ Bu ilerlemeyi bir UI bile\u015fenine veya konsola yazd\u0131rabilirsiniz\n        System.out.print(\"\\r\u0130ndiriliyor: \" + progress + \"% (\" + totalBytesRead + \"\/\" + fileSize + \" byte)\");\n    } else {\n        System.out.print(\"\\r\u0130ndiriliyor: \" + totalBytesRead + \" byte\");\n    }\n}\nSystem.out.println(); \/\/ Yeni sat\u0131ra ge\u00e7mek i\u00e7in<\/code><\/pre>\n<p><code>HttpClient<\/code> ile ilerleme takibi yapmak i\u00e7in, <code>HttpResponse.BodyHandlers.ofFile()<\/code> yerine \u00f6zel bir <code>HttpResponse.BodyHandler<\/code> olu\u015fturmak veya ak\u0131\u015f\u0131 i\u015flerken ilerlemeyi takip etmek gerekebilir. Ancak <code>ofFile<\/code> direkt olarak dosyaya yazd\u0131\u011f\u0131 i\u00e7in, bu durumda yan\u0131t\u0131n <code>InputStream<\/code>&#8216;ini al\u0131p manuel i\u015flem yapmak daha uygun olabilir.<\/p>\n<h3>\u0130ndirmeyi Duraklatma ve Devam Ettirme (Resumable Downloads)<\/h3>\n<p>A\u011f ba\u011flant\u0131s\u0131n\u0131n kesilmesi veya uygulaman\u0131n kapanmas\u0131 gibi durumlarda, b\u00fcy\u00fck bir dosyan\u0131n indirilmesine ba\u015ftan ba\u015flamak yerine kald\u0131\u011f\u0131 yerden devam edebilmek kullan\u0131c\u0131 deneyimini \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131r\u0131r. Bu \u00f6zellik, HTTP protokol\u00fcn\u00fcn <code>Range<\/code> ba\u015fl\u0131\u011f\u0131 sayesinde m\u00fcmk\u00fcnd\u00fcr.<\/p>\n<p>\u0130\u015flem ad\u0131mlar\u0131:<br \/>\n1.  <strong>Sunucu Deste\u011fini Kontrol Etme:<\/strong> \u0130ndirme i\u015flemi ba\u015flamadan \u00f6nce veya ilk indirme iste\u011fi s\u0131ras\u0131nda, sunucunun <code>Accept-Ranges: bytes<\/code> ba\u015fl\u0131\u011f\u0131n\u0131 g\u00f6nderip g\u00f6ndermedi\u011fi kontrol edilir. Bu ba\u015fl\u0131k varsa, sunucu k\u0131smi i\u00e7erik isteklerini destekliyor demektir.<br \/>\n2.  <strong>Mevcut Dosya Boyutunu Alma:<\/strong> E\u011fer dosya daha \u00f6nce k\u0131smen indirilmi\u015fse, yerel dosyan\u0131n mevcut boyutu al\u0131n\u0131r.<br \/>\n3.  <strong>Range Ba\u015fl\u0131\u011f\u0131 Ekleme:<\/strong> <code>HttpRequest<\/code>&#8216;e (HttpClient) veya <code>URLConnection<\/code>&#8216;a <code>Range: bytes=ba\u015flang\u0131\u00e7_bayt\u0131-<\/code> \u015feklinde bir ba\u015fl\u0131k eklenir. \u00d6rne\u011fin, <code>Range: bytes=102400-<\/code> ba\u015fl\u0131\u011f\u0131, dosyan\u0131n 102400. bayt\u0131ndan itibaren indirilmesini ister.<br \/>\n4.  <strong>Yan\u0131t\u0131 \u0130\u015fleme:<\/strong> Sunucu, genellikle <code>206 Partial Content<\/code> HTTP durum kodu ile yan\u0131t verir. Gelen veri, mevcut dosyan\u0131n sonuna eklenir (append mode).<\/p>\n<pre><code class=\"language-java\">\/\/ HttpClient ile k\u0131smi indirme \u00f6rne\u011fi\nimport java.nio.file.StandardOpenOption;\n\/\/ ... di\u011fer import'lar\n\npublic static void resumeDownload(String fileURL, String saveDir, String fileName) throws IOException, InterruptedException {\n    Path savePath = Paths.get(saveDir, fileName);\n    long existingFileSize = 0;\n\n    if (savePath.toFile().exists()) {\n        existingFileSize = savePath.toFile().length();\n        System.out.println(\"Mevcut dosya boyutu: \" + existingFileSize + \" byte. \u0130ndirme devam ettiriliyor.\");\n    }\n\n    HttpClient client = HttpClient.newBuilder()\n            .version(HttpClient.Version.HTTP_2)\n            .connectTimeout(Duration.ofSeconds(5))\n            .build();\n\n    HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()\n            .uri(URI.create(fileURL))\n            .GET()\n            .timeout(Duration.ofSeconds(20));\n\n    if (existingFileSize > 0) {\n        requestBuilder.header(\"Range\", \"bytes=\" + existingFileSize + \"-\");\n    }\n    HttpRequest request = requestBuilder.build();\n\n    try {\n        HttpResponse<Path> response = client.send(request, \n            HttpResponse.BodyHandlers.ofFile(savePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND));\n\n        if (response.statusCode() == 200 || response.statusCode() == 206) {\n            System.out.println(\"Dosya ba\u015far\u0131yla indirildi\/devam ettirildi: \" + response.body().toAbsolutePath());\n        } else {\n            System.err.println(\"\u0130ndirme devam ettirilemedi. HTTP Durum Kodu: \" + response.statusCode());\n            throw new IOException(\"HTTP \u0130ndirme Hatas\u0131: \" + response.statusCode());\n        }\n    } catch (IOException | InterruptedException e) {\n        System.err.println(\"\u0130ndirme devam ettirilirken bir hata olu\u015ftu: \" + e.getMessage());\n        throw e;\n    }\n}<\/code><\/pre>\n<p><code>StandardOpenOption.APPEND<\/code> se\u00e7ene\u011fi, <code>ofFile<\/code> metoduyla dosyaya veri eklenmesini sa\u011flar.<\/p>\n<h3>\u00c7oklu \u0130\u015f Par\u00e7ac\u0131\u011f\u0131 ile \u0130ndirme (Multi-threaded Downloads)<\/h3>\n<p>B\u00fcy\u00fck dosyalar\u0131n indirme h\u0131z\u0131n\u0131 art\u0131rmak i\u00e7in, dosya birden fazla par\u00e7aya b\u00f6l\u00fcnebilir ve her par\u00e7a ayr\u0131 bir i\u015f par\u00e7ac\u0131\u011f\u0131 taraf\u0131ndan e\u015fzamanl\u0131 olarak indirilebilir. Bu, \u00f6zellikle y\u00fcksek bant geni\u015fli\u011fine sahip ancak tek bir ba\u011flant\u0131 \u00fczerinden tam h\u0131z sa\u011flamayan sunuculardan indirme yaparken faydal\u0131 olabilir. Bu y\u00f6ntem de <code>Range<\/code> ba\u015fl\u0131\u011f\u0131n\u0131 kullan\u0131r.<\/p>\n<p>Ad\u0131mlar:<br \/>\n1.  <strong>Toplam Dosya Boyutunu Alma:<\/strong> \u0130lk olarak, <code>Content-Length<\/code> ba\u015fl\u0131\u011f\u0131 \u00fczerinden dosyan\u0131n toplam boyutu al\u0131n\u0131r.<br \/>\n2.  <strong>Par\u00e7alara Ay\u0131rma:<\/strong> Dosya boyutu, belirlenen i\u015f par\u00e7ac\u0131\u011f\u0131 say\u0131s\u0131na b\u00f6l\u00fcnerek her i\u015f par\u00e7ac\u0131\u011f\u0131n\u0131n indirece\u011fi bayt aral\u0131klar\u0131 hesaplan\u0131r.<br \/>\n3.  <strong>E\u015fzamanl\u0131 \u0130ndirme:<\/strong> Her i\u015f par\u00e7ac\u0131\u011f\u0131, kendi aral\u0131\u011f\u0131 i\u00e7in bir HTTP iste\u011fi (<code>Range<\/code> ba\u015fl\u0131\u011f\u0131 ile) g\u00f6nderir ve veriyi ge\u00e7ici bir dosyaya veya do\u011frudan ana dosyan\u0131n ilgili konumuna yazar.<br \/>\n4.  <strong>Par\u00e7alar\u0131 Birle\u015ftirme:<\/strong> E\u011fer her par\u00e7a ayr\u0131 bir ge\u00e7ici dosyaya indirildiyse, t\u00fcm par\u00e7alar indirildikten sonra bu ge\u00e7ici dosyalar s\u0131ras\u0131yla okunarak tek bir ana dosyada birle\u015ftirilir.<\/p>\n<p>Bu y\u00f6ntem, <code>ExecutorService<\/code> ve <code>Future<\/code> gibi Java e\u015fzamanl\u0131l\u0131k API&#8217;lerini kullanarak uygulanabilir. Olduk\u00e7a karma\u015f\u0131k bir yap\u0131ya sahip oldu\u011fundan, burada sadece konsepti anlat\u0131lm\u0131\u015ft\u0131r.<\/p>\n<h3>Proxy Ayarlar\u0131<\/h3>\n<p>Kurumsal a\u011flarda veya belirli g\u00fcvenlik gereksinimleri olan ortamlarda, HTTP isteklerinin bir proxy sunucusu \u00fczerinden ge\u00e7irilmesi gerekebilir. Java, bu ayarlar\u0131 hem sistem \u00f6zellikleri arac\u0131l\u0131\u011f\u0131yla global olarak hem de <code>URLConnection<\/code> veya <code>HttpClient<\/code> \u00fczerinden programatik olarak yapmaya izin verir.<\/p>\n<p><strong>Sistem \u00d6zellikleri ile Global Ayar:<\/strong><\/p>\n<pre><code class=\"language-java\">System.setProperty(\"http.proxyHost\", \"proxy.example.com\");\nSystem.setProperty(\"http.proxyPort\", \"8080\");\nSystem.setProperty(\"https.proxyHost\", \"proxy.example.com\");\nSystem.setProperty(\"https.proxyPort\", \"8080\");\n\/\/ E\u011fer proxy kimlik do\u011frulama gerektiriyorsa\n\/\/ Authenticator.setDefault(new Authenticator() { ... });<\/code><\/pre>\n<p>Bu ayarlar, JVM \u00e7al\u0131\u015ft\u0131r\u0131ld\u0131\u011f\u0131nda t\u00fcm <code>HttpURLConnection<\/code> ve <code>HttpClient<\/code> (varsay\u0131lan yap\u0131land\u0131rmada) isteklerini etkiler.<\/p>\n<p><strong>HttpClient ile Programatik Ayar:<\/strong><\/p>\n<pre><code class=\"language-java\">import java.net.ProxySelector;\nimport java.net.InetSocketAddress;\n\nHttpClient clientWithProxy = HttpClient.newBuilder()\n        .proxy(ProxySelector.of(new InetSocketAddress(\"proxy.example.com\", 8080)))\n        .build();\n\/\/ Bu client ile yap\u0131lan istekler proxy \u00fczerinden ge\u00e7er.<\/code><\/pre>\n<h3>SSL\/TLS Sertifika Sorunlar\u0131<\/h3>\n<p>HTTPS \u00fczerinden dosya indirirken, sunucunun SSL\/TLS sertifikas\u0131n\u0131n ge\u00e7erli ve g\u00fcvenilir olmas\u0131 gerekir. E\u011fer sertifika kendinden imzal\u0131 ise, s\u00fcresi dolmu\u015fsa veya g\u00fcvenilmeyen bir CA taraf\u0131ndan imzalanm\u0131\u015fsa, Java <code>SSLHandshakeException<\/code> gibi hatalar f\u0131rlatabilir.<\/p>\n<p>Geli\u015ftirme ortamlar\u0131nda bu t\u00fcr hatalar\u0131 ge\u00e7ici olarak g\u00f6z ard\u0131 etmek i\u00e7in <code>TrustManager<\/code> ve <code>SSLContext<\/code> kullanarak \u00f6zel bir SSL ba\u011flam\u0131 olu\u015fturulabilir. Ancak, <strong>\u00fcretim ortamlar\u0131nda sertifika do\u011frulamay\u0131 atlamak ciddi g\u00fcvenlik riskleri ta\u015f\u0131r ve kesinlikle \u00f6nerilmez.<\/strong><\/p>\n<pre><code class=\"language-java\">\/\/ Geli\u015ftirme ama\u00e7l\u0131, sertifika do\u011frulamay\u0131 atlayan \u00f6rnek (KES\u0130NL\u0130KLE \u00dcRET\u0130MDE KULLANMAYIN!)\nimport javax.net.ssl.*;\nimport java.security.cert.X509Certificate;\n\nTrustManager[] trustAllCerts = new TrustManager[]{\n    new X509TrustManager() {\n        public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }\n        public void checkClientTrusted(X509Certificate[] certs, String authType) { }\n        public void checkServerTrusted(X509Certificate[] certs, String authType) { }\n    }\n};\n\nSSLContext sslContext = SSLContext.getInstance(\"TLS\");\nsslContext.init(null, trustAllCerts, new java.security.SecureRandom());\n\nHttpClient client = HttpClient.newBuilder()\n        .sslContext(sslContext) \/\/ \u00d6zel SSL ba\u011flam\u0131n\u0131 ayarla\n        .build();<\/code><\/pre>\n<p>Do\u011fru yakla\u015f\u0131m, g\u00fcvenilmeyen sertifikalar\u0131 Java&#8217;n\u0131n g\u00fcven deposuna (<code>cacerts<\/code>) eklemek veya sunucu taraf\u0131ndaki sertifika sorunlar\u0131n\u0131 gidermektir.<\/p>\n<h3>Timeout Ayarlar\u0131<\/h3>\n<p>A\u011f i\u015flemlerinde zaman a\u015f\u0131m\u0131 ayarlar\u0131, uygulaman\u0131n a\u011f sorunlar\u0131 nedeniyle s\u00fcresiz olarak tak\u0131l\u0131 kalmas\u0131n\u0131 \u00f6nlemek i\u00e7in kritik \u00f6neme sahiptir.<\/p>\n<p>*   <strong><code>URLConnection<\/code> i\u00e7in:<\/strong><\/p>\n<pre><code class=\"language-java\">connection.setConnectTimeout(5000); \/\/ Ba\u011flant\u0131 kurma zaman a\u015f\u0131m\u0131 (milisaniye)\n    connection.setReadTimeout(10000);   \/\/ Veri okuma zaman a\u015f\u0131m\u0131 (milisaniye)<\/code><\/pre>\n<p>*   <strong><code>HttpClient<\/code> i\u00e7in:<\/strong><\/p>\n<pre><code class=\"language-java\">HttpClient client = HttpClient.newBuilder()\n            .connectTimeout(Duration.ofSeconds(5)) \/\/ Ba\u011flant\u0131 zaman a\u015f\u0131m\u0131\n            .build();\n\n    HttpRequest request = HttpRequest.newBuilder()\n            .uri(URI.create(fileURL))\n            .timeout(Duration.ofSeconds(20)) \/\/ \u0130stek zaman a\u015f\u0131m\u0131 (ba\u011flant\u0131 ve yan\u0131t\u0131 kapsar)\n            .build();<\/code><\/pre>\n<p>Bu ayarlar, a\u011f gecikmeleri veya sunucu yan\u0131t vermedi\u011finde uygulaman\u0131n belirli bir s\u00fcre sonra hata f\u0131rlatarak kurtulmas\u0131n\u0131 sa\u011flar.<\/p>\n<h2>Hata Y\u00f6netimi ve En \u0130yi Uygulamalar<\/h2>\n<p>Sa\u011flam bir dosya indirme mekanizmas\u0131 olu\u015fturmak i\u00e7in hata y\u00f6netimi ve belirli en iyi uygulamalara uymak esast\u0131r.<\/p>\n<h3>Yayg\u0131n Hatalar ve \u00c7\u00f6z\u00fcmleri<\/h3>\n<p>*   <strong><code>MalformedURLException<\/code>:<\/strong> Ge\u00e7ersiz bir URL dizesi kullan\u0131ld\u0131\u011f\u0131nda olu\u015fur. URL&#8217;nin do\u011fru formatta oldu\u011fundan emin olun.<br \/>\n*   <strong><code>UnknownHostException<\/code>:<\/strong> Host ad\u0131 \u00e7\u00f6z\u00fcmlenemedi\u011finde (DNS hatas\u0131) olu\u015fur. A\u011f ba\u011flant\u0131s\u0131n\u0131 veya URL&#8217;deki host ad\u0131n\u0131 kontrol edin.<br \/>\n*   <strong><code>ConnectException<\/code>:<\/strong> Sunucuya ba\u011flant\u0131 kurulamad\u0131\u011f\u0131nda (sunucu kapal\u0131, g\u00fcvenlik duvar\u0131 engeli vb.) olu\u015fur. Sunucunun eri\u015filebilirli\u011fini ve a\u011f ayarlar\u0131n\u0131 kontrol edin.<br \/>\n*   <strong><code>SocketTimeoutException<\/code>:<\/strong> Ba\u011flant\u0131 veya okuma zaman a\u015f\u0131m\u0131 doldu\u011funda olu\u015fur. Zaman a\u015f\u0131m\u0131 de\u011ferlerini art\u0131rabilir veya a\u011f ko\u015fullar\u0131n\u0131 iyile\u015ftirebilirsiniz.<br \/>\n*   <strong><code>IOException<\/code>:<\/strong> Genel bir I\/O hatas\u0131d\u0131r (a\u011f ba\u011flant\u0131s\u0131 kesilmesi, dosya yazma hatas\u0131, disk dolu vb.). Hata mesaj\u0131n\u0131 inceleyerek spesifik nedeni belirleyin.<br \/>\n*   <strong>HTTP Hatalar\u0131 (4xx, 5xx):<\/strong><br \/>\n    *   <code>404 Not Found<\/code>: \u0130stenen kaynak sunucuda bulunamad\u0131. URL&#8217;yi kontrol edin.<br \/>\n    *   <code>403 Forbidden<\/code>: Kayna\u011fa eri\u015fim izni yok. Kimlik do\u011frulama veya yetkilendirme sorunlar\u0131n\u0131 kontrol edin.<br \/>\n    *   <code>500 Internal Server Error<\/code>: Sunucu taraf\u0131nda bir hata olu\u015ftu. Sunucu y\u00f6neticisi ile ileti\u015fime ge\u00e7in.<br \/>\n    <code>HttpClient<\/code> kullan\u0131rken <code>HttpResponse.statusCode()<\/code> ile bu hatalar\u0131 kolayca yakalayabilirsiniz.<\/p>\n<h3>Kaynak Y\u00f6netimi<\/h3>\n<p>Java&#8217;da dosya ve a\u011f ak\u0131\u015flar\u0131 gibi kaynaklar, do\u011fru \u015fekilde kapat\u0131lmazsa bellek s\u0131z\u0131nt\u0131lar\u0131na, dosya kilitleme sorunlar\u0131na veya sistem kaynaklar\u0131n\u0131n t\u00fckenmesine yol a\u00e7abilir. <code>try-with-resources<\/code> ifadesi, bu t\u00fcr kaynaklar\u0131n otomatik olarak kapat\u0131lmas\u0131n\u0131 sa\u011flayarak bu sorunlar\u0131 \u00f6nler.<\/p>\n<pre><code class=\"language-java\">\/\/ Kaynaklar\u0131 otomatik kapatma \u00f6rne\u011fi\ntry (InputStream in = connection.getInputStream();\n     FileOutputStream out = new FileOutputStream(savePath.toFile())) {\n    \/\/ Dosya indirme mant\u0131\u011f\u0131\n} \/\/ Ak\u0131\u015flar bu noktada otomatik olarak kapat\u0131l\u0131r\ncatch (IOException e) {\n    \/\/ Hata y\u00f6netimi\n}<\/code><\/pre>\n<p>Bu yap\u0131, <code>AutoCloseable<\/code> aray\u00fcz\u00fcn\u00fc uygulayan t\u00fcm nesneler i\u00e7in kullan\u0131labilir ve kodunuzu daha temiz ve g\u00fcvenli hale getirir.<\/p>\n<h3>G\u00fcvenlik Hususlar\u0131<\/h3>\n<p>*   <strong>URL Do\u011frulama:<\/strong> Kullan\u0131c\u0131dan al\u0131nan URL&#8217;leri do\u011frulamak, k\u00f6t\u00fc niyetli veya yanl\u0131\u015f formatlanm\u0131\u015f URL&#8217;lerden kaynaklanabilecek sald\u0131r\u0131lar\u0131 \u00f6nler.<br \/>\n*   <strong>Hedef Dizin G\u00fcvenli\u011fi:<\/strong> \u0130ndirilen dosyalar\u0131n kaydedilece\u011fi dizini dikkatli se\u00e7in. Kullan\u0131c\u0131 girdisiyle hedef yolu olu\u015ftururken &#8220;path traversal&#8221; (dizin atlama) sald\u0131r\u0131lar\u0131na kar\u015f\u0131 dikkatli olun (\u00f6rn: <code>..\/..\/evil_file.sh<\/code>). <code>java.nio.file.Path.normalize()<\/code> ve <code>resolve()<\/code> gibi metotlar bu konuda yard\u0131mc\u0131 olabilir.<br \/>\n*   <strong>\u0130ndirilen Dosya \u0130\u00e7eri\u011fi Do\u011frulama:<\/strong> \u00d6zellikle y\u00fcr\u00fct\u00fclebilir dosyalar veya ar\u015fivler indiriliyorsa, i\u00e7eri\u011fin b\u00fct\u00fcnl\u00fc\u011f\u00fcn\u00fc (checksum\/hash kontrol\u00fc) ve g\u00fcvenli\u011fini (vir\u00fcs taramas\u0131 &#8211; harici ara\u00e7larla) do\u011frulamak \u00f6nemlidir.<br \/>\n*   <strong>Kimlik Do\u011frulama:<\/strong> E\u011fer indirilen kaynak kimlik do\u011frulama gerektiriyorsa (Basic Auth, Bearer Token vb.), bu bilgileri g\u00fcvenli bir \u015fekilde (\u00f6rne\u011fin, ortam de\u011fi\u015fkenleri veya g\u00fcvenli bir yap\u0131land\u0131rma dosyas\u0131 kullanarak) iletin. \u015eifreleri do\u011frudan koda yazmaktan ka\u00e7\u0131n\u0131n.<\/p>\n<h3>Performans \u0130pu\u00e7lar\u0131<\/h3>\n<p>*   <strong>Uygun Tampon Boyutu:<\/strong> <code>InputStream<\/code> tabanl\u0131 indirmelerde kullan\u0131lan tampon boyutu (buffer size), performans \u00fczerinde etkili olabilir. Genellikle 4KB ila 8KB aras\u0131 bir boyut iyi bir ba\u015flang\u0131\u00e7 noktas\u0131d\u0131r, ancak en iyi boyutu bulmak i\u00e7in testler yapmak faydal\u0131 olabilir. \u00c7ok k\u00fc\u00e7\u00fck veya \u00e7ok b\u00fcy\u00fck tamponlar performans\u0131 d\u00fc\u015f\u00fcrebilir.<br \/>\n*   <strong>A\u011f ve Disk I\/O&#8217;sunu Minimize Etme:<\/strong> <code>FileChannel.transferFrom()<\/code> gibi NIO tabanl\u0131 y\u00f6ntemler, \u00e7ekirdek alan\u0131 ile kullan\u0131c\u0131 alan\u0131 aras\u0131ndaki veri kopyalama miktar\u0131n\u0131 azaltarak performans\u0131 art\u0131rabilir.<br \/>\n*   <strong>Asenkron \u0130ndirme:<\/strong> <code>HttpClient.sendAsync()<\/code> veya <code>CompletableFuture<\/code> ile kendi asenkron mekanizman\u0131z\u0131 olu\u015fturarak, uygulaman\u0131n ana i\u015f par\u00e7ac\u0131\u011f\u0131n\u0131n indirme i\u015flemi s\u0131ras\u0131nda bloklanmas\u0131n\u0131 \u00f6nleyebilir, bu da \u00f6zellikle UI uygulamalar\u0131nda veya \u00e7ok say\u0131da e\u015fzamanl\u0131 indirme gerektiren sunucu uygulamalar\u0131nda daha iyi yan\u0131t verebilirlik sa\u011flar.<br \/>\n*   <strong>Ba\u011flant\u0131 Havuzlama:<\/strong> <code>HttpClient<\/code> varsay\u0131lan olarak ba\u011flant\u0131 havuzlama yapar, bu da ayn\u0131 host&#8217;a yap\u0131lan tekrar eden istekler i\u00e7in yeni ba\u011flant\u0131 kurma maliyetini ortadan kald\u0131r\u0131r. <code>URLConnection<\/code> ile bu daha manuel bir \u00e7aba gerektirir.<\/p>\n<h2>Kapsaml\u0131 Bir \u00d6rnek Uygulama: Komut Sat\u0131r\u0131 \u0130ndirici<\/h2>\n<p>\u015eimdiye kadar \u00f6\u011frendiklerimizi birle\u015ftirerek, temel hata y\u00f6netimi ve ilerleme takibi i\u00e7eren basit bir komut sat\u0131r\u0131 dosya indirici uygulamas\u0131 olu\u015ftural\u0131m. Bu \u00f6rnek, Java 11 ve \u00fczeri i\u00e7in <code>HttpClient<\/code> kullanacakt\u0131r.<\/p>\n<pre><code class=\"language-java\">import java.io.IOException;\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.time.Duration;\nimport java.util.Optional;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class CommandLineDownloader {\n\n    private static final int BUFFER_SIZE = 8192; \/\/ 8 KB\n    private static final int PROGRESS_UPDATE_INTERVAL_MS = 500; \/\/ 0.5 saniyede bir ilerleme g\u00fcncellemesi\n\n    public static void main(String[] args) {\n        if (args.length < 2) {\n            System.out.println(\"Kullan\u0131m: java CommandLineDownloader <URL> <HedefDizin>\");\n            System.out.println(\"\u00d6rnek: java CommandLineDownloader https:\/\/example.com\/file.zip .\/downloads\");\n            return;\n        }\n\n        String fileURL = args[0];\n        String saveDir = args[1];\n        String fileName = Paths.get(URI.create(fileURL).getPath()).getFileName().toString();\n\n        if (fileName.isEmpty() || fileName.equals(\"\/\")) {\n            \/\/ URL'den dosya ad\u0131n\u0131 \u00e7\u0131karamazsak varsay\u0131lan bir isim ver\n            fileName = \"downloaded_file_\" + System.currentTimeMillis();\n        }\n\n        Path dirPath = Paths.get(saveDir);\n        if (!dirPath.toFile().exists()) {\n            dirPath.toFile().mkdirs();\n            System.out.println(\"Hedef dizin olu\u015fturuldu: \" + dirPath.toAbsolutePath());\n        }\n\n        Path savePath = Paths.get(saveDir, fileName);\n\n        try {\n            downloadFileWithProgress(fileURL, savePath);\n            System.out.println(\"\\nDosya ba\u015far\u0131yla indirildi: \" + savePath.toAbsolutePath());\n        } catch (IOException | InterruptedException e) {\n            System.err.println(\"\\nDosya indirme i\u015flemi ba\u015far\u0131s\u0131z oldu: \" + e.getMessage());\n            \/\/ E\u011fer indirme ba\u015far\u0131s\u0131z olursa k\u0131smen indirilen dosyay\u0131 silmeyi d\u00fc\u015f\u00fcnebilirsiniz.\n            \/\/ try { Files.deleteIfExists(savePath); } catch (IOException ex) { \/<em> ignored <\/em>\/ }\n        }\n    }\n\n    public static void downloadFileWithProgress(String fileURL, Path savePath) throws IOException, InterruptedException {\n        HttpClient client = HttpClient.newBuilder()\n                .version(HttpClient.Version.HTTP_2)\n                .connectTimeout(Duration.ofSeconds(10))\n                .build();\n\n        \/\/ Resume i\u00e7in mevcut dosya boyutunu kontrol et\n        long existingFileSize = 0;\n        if (savePath.toFile().exists()) {\n            existingFileSize = savePath.toFile().length();\n            System.out.println(\"Mevcut dosya boyutu: \" + existingFileSize + \" byte. \u0130ndirme devam ettirilmeye \u00e7al\u0131\u015f\u0131l\u0131yor...\");\n        }\n\n        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()\n                .uri(URI.create(fileURL))\n                .GET()\n                .timeout(Duration.ofSeconds(60)); \/\/ Toplam istek zaman a\u015f\u0131m\u0131\n\n        if (existingFileSize > 0) {\n            requestBuilder.header(\"Range\", \"bytes=\" + existingFileSize + \"-\");\n        }\n        HttpRequest request = requestBuilder.build();\n\n        AtomicLong downloadedBytes = new AtomicLong(existingFileSize);\n        long totalFileSize = -1; \/\/ Bilinmiyorsa -1\n\n        \/\/ \u0130lerleme g\u00f6stergesi i\u00e7in zamanlay\u0131c\u0131\n        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();\n        scheduler.scheduleAtFixedRate(() -> {\n            if (totalFileSize > 0) {\n                int progress = (int) ((downloadedBytes.get() * 100) \/ totalFileSize);\n                System.out.print(\"\\r\u0130ndiriliyor: \" + progress + \"% (\" + downloadedBytes.get() + \"\/\" + totalFileSize + \" byte)\");\n            } else {\n                System.out.print(\"\\r\u0130ndiriliyor: \" + downloadedBytes.get() + \" byte\");\n            }\n        }, 0, PROGRESS_UPDATE_INTERVAL_MS, TimeUnit.MILLISECONDS);\n\n        try {\n            HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());\n\n            if (response.statusCode() == 200 || response.statusCode() == 206) {\n                Optional<String> contentLengthHeader = response.headers().firstValue(\"Content-Length\");\n                if (contentLengthHeader.isPresent()) {\n                    long currentDownloadSize = Long.parseLong(contentLengthHeader.get());\n                    totalFileSize = (response.statusCode() == 206) ? existingFileSize + currentDownloadSize : currentDownloadSize;\n                }\n                \n                System.out.println(\"\\nHTTP Durum Kodu: \" + response.statusCode() + \". Toplam boyut: \" + (totalFileSize > 0 ? totalFileSize + \" byte\" : \"Bilinmiyor\"));\n\n                try (InputStream in = response.body();\n                     FileOutputStream out = new FileOutputStream(savePath.toFile(), response.statusCode() == 206)) { \/\/ 206 ise append mode\n\n                    byte[] buffer = new byte[BUFFER_SIZE];\n                    int bytesRead;\n                    while ((bytesRead = in.read(buffer)) != -1) {\n                        out.write(buffer, 0, bytesRead);\n                        downloadedBytes.addAndGet(bytesRead);\n                    }\n                }\n            } else {\n                String errorBody = response.body().readNBytes(500).toString(); \/\/ Hata durumunda ilk 500 byte'\u0131 oku\n                throw new IOException(\"HTTP \u0130ndirme Hatas\u0131: \" + response.statusCode() + \", Yan\u0131t: \" + errorBody);\n            }\n        } finally {\n            scheduler.shutdownNow(); \/\/ Zamanlay\u0131c\u0131y\u0131 kapat\n        }\n    }\n}<\/code><\/pre>\n<p>Bu \u00f6rnek, <code>HttpClient<\/code> kullanarak dosya ad\u0131n\u0131 URL&#8217;den otomatik olarak \u00e7\u0131karmay\u0131, hedef dizini olu\u015fturmay\u0131, <code>Range<\/code> ba\u015fl\u0131\u011f\u0131 ile indirmeye devam etmeyi ve basit bir ilerleme g\u00f6stergesi sunmay\u0131 i\u00e7erir. <code>HttpResponse.BodyHandlers.ofInputStream()<\/code> kullanarak yan\u0131t\u0131n <code>InputStream<\/code>&#8216;ini al\u0131p, <code>FileOutputStream<\/code> ile manuel olarak dosyaya yazarak ilerleme takibi yap\u0131lm\u0131\u015ft\u0131r.<\/p>\n<h2>Sonu\u00e7<\/h2>\n<p>Java, uzak bir URL&#8217;den dosya indirme konusunda zengin ve esnek API&#8217;ler sunar. <code>java.net.URL<\/code> ve <code>InputStream<\/code> gibi geleneksel yakla\u015f\u0131mlar basit senaryolar i\u00e7in hala ge\u00e7erliyken, Java 11 ile tan\u0131t\u0131lan <code>java.net.http.HttpClient<\/code> modern uygulamalar i\u00e7in daha g\u00fc\u00e7l\u00fc, performansl\u0131 ve asenkron yetenekler sunar. Se\u00e7im, uygulaman\u0131n gereksinimlerine, Java s\u00fcr\u00fcm\u00fcne ve istenen karma\u015f\u0131kl\u0131k seviyesine ba\u011fl\u0131 olacakt\u0131r.<\/p>\n<p>Bu makalede ele al\u0131nan temel ve geli\u015fmi\u015f y\u00f6ntemler, ilerleme takibi, indirmeye devam etme, proxy ayarlar\u0131 ve hata y\u00f6netimi gibi konular, sa\u011flam ve g\u00fcvenilir bir dosya indirme mekanizmas\u0131 olu\u015fturman\u0131z i\u00e7in gerekli bilgileri sa\u011flam\u0131\u015ft\u0131r. Her zaman en iyi uygulamalar\u0131 (kaynak y\u00f6netimi, g\u00fcvenlik) takip etmek ve uygulaman\u0131z\u0131n \u00f6zel ihtiya\u00e7lar\u0131na g\u00f6re do\u011fru ara\u00e7lar\u0131 ve teknikleri se\u00e7mek \u00f6nemlidir. \u0130nternet \u00fczerinden veri al\u0131m\u0131, modern yaz\u0131l\u0131m geli\u015ftirmenin temel bir par\u00e7as\u0131 olmaya devam ettik\u00e7e, Java&#8217;n\u0131n sundu\u011fu bu yetenekler geli\u015ftiriciler i\u00e7in vazge\u00e7ilmez olacakt\u0131r.<\/body><\/p>\n","protected":false},"excerpt":{"rendered":"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber\nGiri\u015f\nG\u00fcn\u00fcm\u00fcz\u00fcn birbirine ba\u011fl\u0131 d\u00fcnyas\u0131nda, uygulamalar\u0131n internet \u00fczerinden veri al","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1408],"tags":[],"class_list":{"0":"post-31627","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-java","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Java ile URL&#039;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber - Kodlar\u0131n Gizemli D\u00fcnyas\u0131<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Java ile URL&#039;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber\" \/>\n<meta property=\"og:description\" content=\"Java ile URL&#039;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber Giri\u015f G\u00fcn\u00fcm\u00fcz\u00fcn birbirine ba\u011fl\u0131 d\u00fcnyas\u0131nda, uygulamalar\u0131n internet \u00fczerinden veri al\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2025-10-11T23:41:01+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"21 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber\",\"datePublished\":\"2025-10-11T23:41:01+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\"},\"wordCount\":2451,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"articleSection\":[\"Java\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#respond\"]}],\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\",\"name\":\"Java ile URL'den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber - Kodlar\u0131n Gizemli D\u00fcnyas\u0131\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2025-10-11T23:41:01+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Java ile URL'den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/","og_locale":"tr_TR","og_type":"article","og_title":"Java ile URL'den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber","og_description":"Java ile URL'den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber Giri\u015f G\u00fcn\u00fcm\u00fcz\u00fcn birbirine ba\u011fl\u0131 d\u00fcnyas\u0131nda, uygulamalar\u0131n internet \u00fczerinden veri al","og_url":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2025-10-11T23:41:01+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"21 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber","datePublished":"2025-10-11T23:41:01+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/"},"wordCount":2451,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"articleSection":["Java"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#respond"]}],"copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/","url":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/","name":"Java ile URL'den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2025-10-11T23:41:01+00:00","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/java-ile-urlden-dosya-indirme-kapsamli-bir-rehber\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Java ile URL&#8217;den Dosya \u0130ndirme: Kapsaml\u0131 Bir Rehber"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/31627","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=31627"}],"version-history":[{"count":1,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/31627\/revisions"}],"predecessor-version":[{"id":31628,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/31627\/revisions\/31628"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=31627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=31627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=31627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}