Java’da dosya okuma , yazma , kopyalama işlemleri için bir çok farklı sınıf kullanılır. Ve bu sınıflar performansları açısından farklılık göstermektedir. Özellikle Java’nın ilk zamanlarında oluşturulan sınıflar ile , Java 7 ‘de kullanılan sınıflar arasında performans açısından gözle görülebilir farklılıklar bulunmaktadır.
Java’da dosya kopyalaması yaparken kullanılabilecek farklı yaklaşımlar bulunmaktadır.
- Byte Byte okuyup yazma
- Bir bütün olarak okuyup yazma
- Tampanlama kullanarak okuyup yazma.
Bu 3 yaklaşımdan en performanssız olanı şüphesiz ki Byte byte okuyup yazmaktır. Sebebi her okunan byte sonrasında sistemin birde yazma işlemi yapması gerekmektedir. Bunun için önce hardiske erişip okuma işlemi yapması , daha sonra okuduğu veriyi yine hardiske erişerek yazması gerekmektedir. Hardisklerin oldukça yavaş aygıtlar olduğu düşünülürse harcanacak zamanı az çok tahmin edebiliriz. Bu nedenle bu yöntem önerilmeyen bir yöntemdir. Çok küçük boyutta dosya okuma işlemleri yapılacaksa , ve performans çok önemli değilse kullanılabilir.
İkinci yakşım ise dosyayı bir bütün olarak okumak , ve tek seferde yazmaktır. Bu yaklaşım aslında düşünüldüğünde en mantıklı gelen yaklalım olabilir. Bir düşünelim dosyayı okuyacağız, ve tek bir sefer hardiske ulaşarak o dosyayı yazacağız. Kulağa ne kadar hoş geliyor öyle değil mi ? Ama aynı zamanda çokta tehlikeli. Çok büyük boyutlu bir dosya ile çalıştığımızı hesap edelim , ve bu dosya boyutu bilgisayardaki Ram miktrınıda aşıyor bulunsun ? Bu durumda okuma işlemi yaparken RAM tamamen dolacak ve bilgisayar kaynaksızlıktan kağnı gibi işlem yapar hale gelecektir. Bu nedenle bu yaklaşımda pek tercih edilmemektedir.
En iyi kopyalama yaklaşımını en sona sakladım 🙂 Bu yontemde bir tampon boyutu belirlenir ve bu tampon boyutu büyüklüğünde bir byte dizisi oluşturulur. Byte dizisi oluşturmamızın sebebi dosyadan byte olarak okuma işlemi yapacak olmamızdandır. Java’da byte olarak okuma ve yazma işlemi yapmamızı sağlayan sınıflar InputStream, ve OutputStream sınıflarıdır. Biz bu iki sınıftan türeyen FileInputStream ve FileOutputStream sınıfını kullanacağız. Oluşuracağız sınıfta bir kaynak dosya, ve bir hedef dosya ismi vereceğiz. Program çalışmaya başladığında kaynak dosyanın var olup olmadığını kontrol edecek, eğer bir hata olması durumunda İstisna (Exception) fırlatacaktır.
Oluşturulan tampon boyutu keyfidir, Statik olarak tanımlamak yerine sisteme özel bir tasarım yapılabilir. Bir takım komutlar ile sistem belleğinin boyutu alınabilir ve belli bir yüzdelik dilim bu tampon için ayırabilir. Ben 2000 byte’lik bir alan ayırmayı uygun gördük. Peki ne bu 2000 byte ne anlama gelecek.
Diyelim ki biz 6000 Byte boyutunda bir dosya kopyalamaya çalışalım aşağıdaki sınıfta while dongusu içinde okuma islemi yapılmaya başlanacak ve bun tampon dolduğunda FileOutputStream’ın Write metodu calişacaktır. 6000 Byte’lık bir kopyalama söz konusu olduğundan hardiske sadece 3 defa yazma işlemi yapılacak ve performansta artış sağlanmış olacaktır. Eğer ilk yaklaşım kullanılsa idi bu islem için 6000 bin defa okuma , 6000 defa yazma işlemi yapılacak ve performans kaybı söz konusu olacaktır.
Şimdi kodları paylaşalım.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class dosyaKopyala {
private static final int TAMPONBOYUTU = 2000;
// Verilerimizi byte byte yazmak performans kaybı olacaktır. Bu nedenle byteların
// bir arada bulundugu bir ön bellekleme yöntemi ile verileri okumak ve yazmak
// cok daha performanslı olur.
private static byte onBellek[] = new byte [TAMPONBOYUTU];
// 2000 boyutlu bir byte dizisi olusturuyoruz. Byte dizisi olusturmamızın sebebi byte byte okuma
// yapacak olmamamız. Input Stream , OutputStream sınıfları byte byte okuma yapar.
public static void dosyaYedekle(String kaynak, String hedef) throws IOException
{
try{
InputStream okuyucu = null;
OutputStream yazici = null;
// Dosya islemlerinde kullanılabilecek bir cok sınıf vardır. fakat dosya kopyalaması
// yaparken özellikle de byte byte okurken Onbellek yeteneği olan sınıfları kullanırız. Bu tip
// sınıflar "Stream" olarak son ek alırlar. ve InputStream , OutputStream sınıflarından türerler.
okuyucu = new FileInputStream(kaynak);
yazici = new FileOutputStream(hedef);
// kaynak ve hedef dosya gosterildi.
while(true)
{
// sonsuz bir dongu kurulması saglanarak dosya kopyalamaya baslayacağız.
// ve dosya sonua geldiğinde döngüyü kırıp çıkacağız
int okunan = okuyucu.read(onBellek);
// okuma islemini byte byte yapmak yerine ön bellek sistemi kullaniyoruz
// okuyu.read() yaptıgımızda bu yordam her okudugu veriyi direk yazmak gerine 2000 li
// bloklar halinde yazma islemi yapılacak.
if(okunan==-1){
break;
// dikkt edilirse okuyucu.read() bir integer dondurur ve bu integer deger aslında bizim
// okudumuz dosyanın byte olarak karsılıklarıdır.
// eğer okudumuz değer -1 ise , dosyanın sonu gelmiş demektir.döngüyü kırmamız gerekir
}
yazici.write(onBellek);
// 20000 li blok dosyaya yazıldı.
}
okuyucu.close(); // okuyucu kapat
yazici.close(); // yazıyı kapat.
}catch (IOException e){
throw new IOException();
}
}
// Bu yordamdan kaynak dosya adı , ve hedef dosya adı elle girilir
// Dikkat kaynak dosya ile program aynı dizinde olmalıdır.
public static void main(String[] args) {
try{
dosyaYedekle("jandarma.Jpeg", "jandarmaKopya.Jpeg");
} catch(IOException e){
System.out.println("Dosya kopyalanırken hata olustu."+ e.getLocalizedMessage());
}
System.out.println("Dosya Kopyalam başarılı..");
}
}
Kaynak Kodları indirmek için aşağıdaki tıklayın.. Sağlıcakla
https://www.dropbox.com/s/44u2yk3hs652yop/Dosya%C4%B0slemleri.rar
Share and Enjoy