Servlet – Yerel Dosya Yayınlama

Yer yer ayrı işlemlerin ürettiği dosyaları, web uygulamanızda yayınlamanız gerekebiliyor. İlk düşündüğünde çok kullanılan ya da kullanılacak birşey gelmesede emin olun karşınıza çıkıyor. Bense buna ilk IVR (Interactive Voice Response) uygulamasının ürettiği ses dosyalarına download linki vermem gerektiğimde rastladım. Bazen sistem öyle bir hal alıyor ki uygulamanızı destekleyen ek sistemler sizin istediğiniz lokasyonlara ürettikleri dosyaları kaydedemiyorlar. İşte böyle durumlarda devreye sizin girip, yerel diskinizde bulunan bir dosyayı yayımlamanız, onun download linkini kullanıcıya sunmanız gerekebiliyor.

Yerel diskinizden bir dosyayı yayınlamanızın temelde iki yolu var. Bunlardan birincisi, ki büyük ihtimalle en kolay ve çabuk yolu, servlet container’ınıza ilgili klasörün tamamını yayınlamasını söylemek. Biz örneğimizde Tomcat kullanıp bunu nasıl yapabileceğinizi incelleyeceğiz. İkinci yolu ise biraz daha zahmetli fakat güvenlik açısından daha doğru bir yol. İkinci yolda servlet üzeirnden bir dosyayı nasıl kullanıcıya download ettireceğinize bakacağız.file_download_servlet

Hazırlık

Hangi yöntemimizi kullanırsak kullanalım bize öncelikle bir web projesi gerekiyor. Projemizi geliştirirken Tomcat 7, Eclipse ve Maven kullanacağız. Bu bilgiler ışığında maven projesinizi aşağıdaki bilgilerle oluşturmanız gerekiyor.

  • Maven Archetype: maven-archetype-webapp
  • Group Id: com.bahadirakin
  • Artifact Id: download-servlet
  • Package: com.bahadirakin.web

Projemizi hazırladığımıza göre ilk yolu anlatmaya başlayabilirz.

Tomcat ile Yerel Dosya Yayımlama

Tomcat ile yerel dizin yayınlamanın, el ile bir web uygulamasını yayınlamaktan neredeyse hiç bir farkı yok. Örneğin bizim yayınlamak istediğimiz dosyalar C:\Content dizininde olsun ve biz bu dizinin içeriğini local uzantısında yayınlamak istiyor olalım. Bunun için tek yapmanız gereken, $CATALINA_HOME/conf/server.xml dosyasının içerisine aşağıdaki satırı eklemek. Bu satırı Host taginin içerisine Valve taginden sonraya eklerseniz sorun yaşamazsınız.

server.xml

<Context path="/local" docBase="C:/Content" />

Bu işlemi yapıp sunucunuzu tekrar başlattığınızda C:\Content\test.txt dosyasına aşağıdaki gibi bir link vererek, test.txt dosyasını download ettirebilirsiniz.

  • http://localhost:8080/local/test.txt

Eğer test.txt parametrik bir değerse yine bir servlet yazmak ya da kullandığınız web yapısıyla bu linki üretmeniz gerekecektir.

Servlet ile Yerel Dosya Yayımlama

Gelelim ikinci kısma bu kısım ilkine göre biraz daha uğraş gerektiriyor. Temel olarak yapmanız gereken bir servlet yaratmak ve ardından local diskteki dosyaya ait InputStream nesnesinin içeriğini, HttpResponse nesnesine ait OutputStream nesnesine kopyalamak. Tabi bunu yaparken dosyaya ait içerik bilgilerini ve header bilgilerini korumanız gerekiyor. Eğer neyi ayarlamanız gerektiğini biliyorsanız çocuk oyuncağı haline geliyor.

Öncelikle servlet sınıfımızı yaratalım. com.bahadirakin.web paketinin içerisine DownloadServlet.java sınıfını yaratalım. Bu sınıfın HttpServlet sınıfından türemesi gerekmektedir. Eğer HttpServlet sınıfını projeniz bulamıyorsa, ya servlet container olarak Tomcat server’ınızı gösterin ya da maven için aşağıdaki bağımlılığı ekleyin.

pom.xml

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

Burada ufak bir not düşmek istiyorum. Hem projenizde hem Tomcat içerisinde servlet-api kütüphanesi varsa Tomcat hata vermez otomatik olarak projeniz içerisindeki servlet-api kütüphanesini gerçerli sayar. Fakat benim kullandığımla Tomcat’in ki arasında çok fark olmayacağından provided socpe’unda bağımlılığımı yükledim.

Bağımlılık sorunumuzuda çözdüğümüze göre artık web.xml sınıfı içerisinde servletimizi tanımlayabilirsiniz. İsterseniz web.xml’e hiç bulaşmayıp ve servlet 3.0 kütüphanesi kullanıp annotation ile de bu işi halledebilirsiniz. Fakat ben genel olarak 2.5 üzerinde çalıştığımda web.xml ile servlet’imi tanıtacağım.

web.xml

<!--?xml version="1.0" encoding="ISO-8859-1"?--><?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<display-name>Download Servlet Application</display-name>

<servlet>
<servlet-name>download</servlet-name>
<servlet-class>com.bahadirakin.web.DownloadServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>download</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>

Esas indirme işlemini yapacak DownloadServet sınıfımız ise aşağıdaki gibidir.

package com.bahadirakin.web;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class DownloadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;
private static final int BUFFER_SIZE = 10240; // 10KB

private String contentFolderPath;

@Override
public void init() throws ServletException {
// Local diskinizdeki dosya yolu.
contentFolderPath = "C:\\Content";
super.init();
}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

// FileName parametre olarak gönderiliyor.
String fileName = request.getParameter("fileName");

// Eğer gönderilen parametre boşsa hata döndürülüyor
if (fileName == null || "".equals(fileName)) {
// Loglama ya da artık ne yapılmak isteniyorsa.
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
// Başka error sayfalarınız varsa onlara yönlendirebilirsiniz.
return;
}

// Sonra istenilen dosya ile content dosya yolu birleştiriliyor.
String requestedFile = contentFolderPath + File.separator + fileName;
File file = new File(requestedFile);

// Eğer dosya bulunmuyorsa yine hata döndürülüyor.
if (!file.exists()) {
// Loglama ya da artık ne yapılmak isteniyorsa.
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
// Başka error sayfalarınız varsa onlara yönlendirebilirsiniz.
return;
}

// Dosya adından content type belirleniyor.
String contentType = getServletContext().getMimeType(fileName);
// TODO Eğer content type ile ilgili controller yapacaksanız tam zamanı.

if (contentType == null) {
contentType = "application/octet-stream";
}

// Response Hazırlanıyor.
response.reset();
response.setBufferSize(BUFFER_SIZE);
response.setContentType(contentType);
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\""
+ fileName + "\"");
copyStreams(new FileInputStream(file), response.getOutputStream());
}

// Utility metodları
public static void copyStreams(InputStream source, OutputStream destination)
throws IOException {
// Prepare streams.
BufferedInputStream input = null;
BufferedOutputStream output = null;

try {
// Open streams.
input = new BufferedInputStream(source, BUFFER_SIZE);
output = new BufferedOutputStream(destination, BUFFER_SIZE);

// Write file contents to response.
byte[] buffer = new byte[BUFFER_SIZE];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} finally {
close(output);
close(input);
}
}

public static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

Burdan sonra tek yapılması gereken localhost:8080/download-servlet/download?fileName=test.txt adresinin çağırılmasıdır. Burdan sonrasını tomcat ve borwser’ınız kendi başına halledecektir.gitcat

Son olarak projenin tamamını aşağıdaki git deposundan indirebilirsiniz.

  • https://github.com/bhdrkn/Java-Examples/tree/master/download-servlet

End Of Line