pillinetwork hesabınızla giriş yapın.

PHP İle Zip Dosyalarını Açmak

Bazı web sitelerinin siz bir ZIP dosyası gönderdiğinizde ilgili dosyayı açıp içindeki dosyaları sitenin tercihlerine göre işlediğine şahit olmuşunuzdur. NETTUTS'ta yayımlanan bu makale ile sizlere PHP kullanarak Zip dosyalarının nasıl açılabileceğini açıklamaya çalışacağım.

Yapmamız Gerekenler

  • Kullanıcıların zip dosyası seçmelerini sağlayacak basit bir upload formu oluştur
  • Kullanıcının zip dosyası seçtiğinden emin ol ve ardından onu tekil bir dosya olarak kaydet.
  • Zip'in içindeki dosyaları bir klasöre çıkar
  • Zip dosyasını sil ve içeriğini göndericiye göster.

Adım 1: Upload Formunu Oluşturmak

\
  • Yeni bir dosya oluşturun ve onu index.html olarak kaydederek sitenizin root klasörüne kaydedin.
  • Sırada, basit bir form oluşturmak var. Aşağıdaki kodu yapıştırın.

1
2
3
4
<form enctype="multipart/form-data" action="" method="post">
<input type="file" name="fupload" /><br />
<input type="submit" value="Upload Zip File" />
</form>

Kullanıcılarınızın dosya göndermesine izin vereceğinz her zaman, form etiketindeki "enctype" değerini "multipart/form-data" olarak değiştirmeniz gerekir. Ardından formun hedefini forumun bulunduğu sayfaya yönlendirecek, ve "method" değerini "post" olarak belirteceğiz.

\

Her şeyi mümkün olduğu kadar sade tutabilmek için sadece dosya giriş alanı ve gönder düğmemiz var. Dosya giriş alanının adını "fupload" olarak belirlediğimize dikkat edin. Birazdan bu değeri sayfanın geri gönderilip gönderilmediğini kontrol etmek için kullanacağız.

Adım 2: PHP'yi Yazmak

index.php sayfanızın en üzerinde, doctype değeri henüz belirtilmeden aşağıdaki kodu girin:

1
2
3
4
5
<?php
if(isset($_FILES['fupload'])) {
$filename = $_FILES['fupload']['name'];
$source = $_FILES['fupload']['tmp_name'];
$type = $_FILES['fupload']['type'];

Adım adım anlatalım:

  • Eğer "fupload" adındaki input etiketi belirtilmişse, o zaman aşağıdaki kodu çalıştır, belirtilmemişse hiçbir şey yapma.
  • Şimdi birkaç değişken oluşturmamız gerek. Gönderilen dosyanın adını almamız, dosyanın geçiçi olarak depolanacağı klasörü ve seçili dosyanın türünü belirlememiz gerek.

Diyelim kullanıcı "myPics.zip" adında bir dosya seçmiş olsun.

  • $_FILES['fupload']['name'] = "myPics.zip"
  • $_FILES['fupload']['tmp_name'] = dosyanin sunucuda depolanacagi gecici alan.
  • $_FILES['fupload']['type'] = "application/zip". (Bu deger dosyanin gonderildigi tarayiciya göre degisiklik gosterecektir.
\

Şimdi, dosya adını ve uzantısını ayıralım:

1
2
$name = explode('.', $filename);
$target = 'extracted/' . $name[0] . '-' . time() . '/';

"myPics.zip" dosya örneğimizde olduğu gibi - $name[0] "myPics" değerini, $name[1] "zip" değrini alacak.

Şimdi, $target adında bir değişken oluşturalım. Bu değişken dosyamızın sunucuda kaydedilecek konumunu belirtsin. Başka bir kullanıcının "myPics" adında bir dosya göndererek halihazırda sunucuda bulunan "myPics" dosyasının üzerine yazmasını enlemek için gönderilen dosyanın tekil bir yere kaydedilmiş olduğundan emin olmamız gerek. Bu görevi yerine getirebilmemiz için "time()" fonksiyonunu dahil edeceğiz. Eğer bu değeri gönderilen dosyanın adına dahil edersek her zaman tekil bir klasör adı kullanılacağından emin olabiliriz.

$target = "dosyalar/myPics-31122009/"

Adım 3: Zip Dosyasının Seçili Olduğundan Emin Ol

$target değrinden hemen sonra aşağıdaki kodu yapıştırın:

1
2
3
4
5
6
7
8
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/s-compressed');
foreach($accepted_types as $mime_type) {
if($mime_type == $type) {
$okay = true;
break;
}
}

  • $accepted_types dizisini oluşturarak başlıyoruz. Hızlı bir "zip mime types" Google araması bize dört adet değeri veriyor: 'application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/s-compressed'. Her tarayıcının dosya türlerini kaydetmek için kendine özgü yöntemleri bulunuyor. Biz ileride olası hataları önlemek için her değeri kontrol etmek zorundayız.
  • Bunu yapmak için, öncelikle dizimizdeki öğelerden herhangi birinin "$_FILES['fupload']['type'] " değerine sahip olup olmadığını kontrol etmemiz lazım. Eğer sahipse, o zaman "$okay" değişkenimizi 'true' olarak belirleyecek - ve 'for' komutu ile de kullanıcının zip dosyasını seçtiğinden emin olacağız.

Ne yazık ki Safari ve Chrome tarayıcları zip dosyaları için türü kaydetmiyorlar. Bu da bizim için bir problem oluşturuyor. Biraz araştırmadan sonra, PEAR uzantısını kullanmadan basit bir çözüm bulamadım. Bunun yerine biz de en azından dosya adının .zip ile bittiğinden emin olmamız gerekecek. Bunun %100 güvenli olmadığını bilyorum. Ya kullanıcı uzantısı .zip olan başka bir dosya gönderirse? Yorumlarınızı bekliyorum.

1
$okay = strtolower($name[1]) == 'zip' ? true: false;

  • Bu komutu tek bir satırda kullanabilmek için üçlü operatörü kullanacağız. Eğer ($name[1]) dosyasının uzantısı 'zip'e eşitse, $okay değişkeninin değerini true olarak atayacağız. Değilse 'false' olacak.

Sırada $okay değişkeninin değerinin false olup olmadığını kontrol edeceğiz. Eğer false ise 'zip' dosyasının seçilmediğini anlayacağız. Bu durumda PHP'ye kodu işlemeyi durdurmasını söyleyeceğiz.

1
2
3
if(!$okay) {
die("Lütfen bir zip dosyası seçin!");
}

Adım 4: Zip Dosyasını Kaydetmek

1
2
3
4
5
6
7
8
mkdir($target);
$saved_file_location = $target . $filename;
if(move_uploaded_file($source, $saved_file_location)) {
openZip($saved_file_location);
} else {
die("Bir problem oluştu!");
}

  • Öncelikle $target değerinden aldığımız dizini oluşturmamız gerek. Bu kolayca "mkdir" komutu ile gerçekleştirilebiir.
  • Sırada gönderilen dosyayı geçici klasörden $target dizinimize taşımayı deneyelim. Eğer bu işlem başarıyla tamamlandıysa, "openZip" fonksiyonunu çağırabiliriz.

Adım 5: openZip() Komutu

Yeni bir sayfa oluşturun ve "functions.php" adıyla kaydedin, sonra aşağıdaki kodu yapıştırın.

\

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function openZip($file_to_open) {
global $target;
$zip = new ZipArchive();
$x = $zip->open($file_to_open);
if($x === true) {
$zip->extractTo($target);
$zip->close();
unlink($file_to_open);
} else {
die("Bir problem oluştu. Lütfen tekrar deneyin!");
}
}
?>

Bu fonksiyon sadece bir parametreyi kontrol edecektir: $file_to_open. Bu parametre açmaya çalıştığımız zip dosyasının konumunu içerecektir.

  • ZipArchive() sınıfını kullanabilmek için öncelikle "php.ini" dosyanızda 'php_zip.dll' dosyasını aktif hale getirdiğinizden emin olun. 'php_zip.dll' terimini "php.ini" dosyası içerisinde aratın ve ardından tırnak işaretlerini kaldırın.
\
  • Her sınıfta olduğu gibi nesne için yeni bir örnek yaratmamız gerekecek. Şimdi "open" metodunu kullanacak ve dosyanın açılması gerektiği konumu aktaracağız. Eğer başarıyla gerçekleştiyse bu metod bize 'true' değerini verecektir. Eğer durum buysa o zaman zip dosyasının içeriğini klasöre açacağız. Bu klasörün konumunu $target değişkenini oluştururken belirtmiştik. (Not: Bu değişkene ulaşabilmek için değişkenin önüne "global" değerini eklemeniz gerekiyor. Bu kod PHP'ye geçerli fonksiyonun dışına çıkmasını ve $target değişkenini aramasını söylüyor).
  • Tüm dosyalar aktarıldığında zip dosyasını sileceğiz, zira artık onunla bir işimiz kalmadı.

Unutmayın - functions.php dosyamızı oluşturduk ancak onu dahil etmemiz gerekiyor. Aşağıdaki kodu 'index.php' sayfanızın üst kısmına, PHP açılış etiketinin hemen ardına ekleyin.

1
require_once 'functions.php';

İçeriği Yazdırmak

\

Şimdiye kadarki her şey sorunsuz çalışıyor. Ancak size biraz geribildirim vermek için, yeni dizini taratalım ve içeriğini ekrana yazdıralım. Aşağıdaki kodu projenizden silmeniz gerek, zira bu kod sadece test etmeniz için var. Eğer kodu kullanmak istiyoesanız, o zaman bilgiyi echo kullanarak body etiketi içerisinde yazdırın.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$scan = scandir($target . $name[0]);
print '<ul>';
for ($i = 0; $i<count($scan); $i++) {
if(strlen($scan[$i]) >= 3) {
$check_for_html_doc = strpos($scan[$i], 'html');
$check_for_php = strpos($scan[$i], 'php');
if($check_for_html_doc === false && $check_for_php === false) {
echo '<li>' . $scan[$i] . '</li>';
} else {
echo '<li><a href="' . $target . $name[0] . '/' . $scan[$i] . '">' . $scan[$i] . '</a></li>';
}
}
}
print '</ul>';
}

Bu son bölüm hakkında çok konuşmayacağım - çünkü gerekli değil. Çok kısaca, yukarıdaki betik yeni dizinimizi tarıyor ve içeriğini sırasız liste olarak ekrana yazdırıyor.

Bitti

Çok zor değildi, değil mi? Projelerinize kolayca dahil edebileceğiniz kullanışlı bir özellik. Zip dosyaları gönderirken oluşabilecek olası güvenlik açıklarına yönelik görüşleriniz neler? Zip dosyasının içeriğinin zararsız olacağından nasıl emin olabiliriz? Bu güvenlik konularında herkesin yorumlarını duymak isterim. Eğer dikkatli bir şekilde kullanılmadığında bir bilgisayar korsanı sunucunuza ciddi zararlar verebilir. Gelin tartışalım! Tehlikeli dosyaları aramak (.htaccess dosyaları) ve silmek mümkün.

index.php Sayfasının Son Hali

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
include 'functions.php';
if(isset($_FILES['fupload'])) {
$filename = $_FILES['fupload']['name'];
$source = $_FILES['fupload']['tmp_name'];
$type = $_FILES['fupload']['type'];
$name = explode('.', $filename);
$target = 'extracted/' . $name[0] . '-' . time() . '/';
// Ensures that the correct file was chosen
$accepted_types = array('application/zip',
'application/x-zip-compressed',
'multipart/x-zip',
'application/s-compressed');
foreach($accepted_types as $mime_type) {
if($mime_type == $type) {
$okay = true;
break;
}
}
//Safari and Chrome don't register zip mime types. Something better could be used here.
$okay = strtolower($name[1]) == 'zip' ? true: false;
if(!$okay) {
die("Please choose a zip file, dummy!");
}
mkdir($target);
$saved_file_location = $target . $filename;
if(move_uploaded_file($source, $saved_file_location)) {
openZip($saved_file_location);
} else {
die("There was a problem. Sorry!");
}
// This last part is for example only. It can be deleted.
$scan = scandir($target . $name[0]);
print '<ul>';
for ($i = 0; $i<count($scan); $i++) {
if(strlen($scan[$i]) >= 3) {
$check_for_html_doc = strpos($scan[$i], 'html');
$check_for_php = strpos($scan[$i], 'php');
if($check_for_html_doc === false && $check_for_php === false) {
echo '<li>' . $scan[$i] . '</li>';
} else {
echo '<li><a href="' . $target . $name[0] . '/' . $scan[$i] . '">' . $scan[$i] . '</a></li>';
}
}
}
print '</ul>';
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<head>
<title>How to Upload and Open Zip Files With PHP</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div id="container">
<h1>Upload A Zip File</h1>
<form enctype="multipart/form-data" action="" method="post">
<input type="file" name="fupload" /><br />
<input type="submit" value="Upload Zip File" />
</form>
</div><!--end container-->
</body>

/* Etiketler: , , , , */
/* pinkfloyd yazdı. 01 Ocak 2009 09:50. 4 yorum var */

Yorumlar

Birincisi dosya ya da klasör ismi çakışması durumunu engellemek için isim içine tarih eklediniz. Ama bu tarih değeri bence yeterli değil. Ufak çapta ve az ziyaretçisi olan sitelerde sorun olmayacaktır ama yoğun ziyaretçisi olan sitelerde örneğin bir yılbaşı günü geldiğinde yüzlerce "cristmas" isimli dosya gelecektir. içine her ne kadar tarih eklesek bile çakışma olasılığı oldukça yüksek. Bu tarih ile birlikte saat dakika ve saniye değerlerini (hatta mümkünse salise) eklemek gerekir.

İkinci olarak dediğiniz gibi dosya uzantısını kontrol etmek pek bir şeyi değiştirmeyecektir. Zira amacı hack olan birisi zaten sizin uzantı kontrolü işleminizi rahatlıkla aşıyor. En çok dikkatsiz kullanıcıların .zip değil .jpg yüklemesini önlemiş olur. Ama bunun için server tarafında kasmaya gerek yok ki zaten. javascript ile de bunu server'ı hiç yormadan yaptırabiliriz. Ama bunun ile hedef kitlesi sadece hack bilgisi olmayan kötü amacı olmayan kullanıcıların yanlışlıklarını absorbe edebiliriz.

Benim fikrime göre yüklenen dosya binary olarak açılıp dosya başlığı okunabilirse ancak bu şekilde bir çözüm üretilebilir. zip algoritması format bilgilerini içeren başlığını sıkıştırmıyor diye biliyorum. her neyse ama bu sefer de server bu dosyayı açtığında zip'li bir içerik yerine virüs koduna rastlar ise bu da belleğe alınacak, belleğe sızmayı başaran kod kendini bir şekilde işlemciye çalıştırabilecektir. Sonuç olarak bu da bir çözüm olmaktan çıkar.

Bir başka yol da server tarafındaki antivirüsleri script içinden ve yüklenen dosyaları kontrol etmesi için tetiklemek gerekir. Belki her gün belli saatlerde topluca bir kontrol yapılabilir performans açısından. Yani kullanıcıları biraz bekletiriz. Bileşen desteği olan antivürüsler kullanmak gerekecektir. Ama sanırım en garantili yöntem bu olsa gerek..

/* if life="" then call BatsinBuDunya() */

Nettuts'daki yazının yorumlarında da söyledik, burada da söyleyelim, zip açmak için bundan daha kötü bir yol seçilemez.

Dosyanın zip olduğunu, bir oradan 1 buradan kontrol edelim diyemessiniz, merkezi ve tam çalışan bir yol bulmanız lazım. Ben çalışma izni olmayan 1 klasöre yükleyip php nin fonksiyonları ile mime type ı kontrol etmeyi tercih ediyorum.

Dosya adında birden fazla . var ise, benim.sevgili.dosyam.zip uzantı testini de geçemeyecektir.

@lazaronnie: time() ile aynı sn de aynı isimle birden fazla dosya yüklenmesi imkansız.

Dosya adında türkçe karakter var ise özellikle unix sunucularda dosya bulunamıyor hatası alır kullanıcılar. Bir çok sunucunun tr lokali olmadan derlendiğini unutmamak lazım.

/* gCg - Güney Can Gökoğlu [gcg.me] */

bence mukemmel bir yazi olmus..anlamadigim noktalardan bir tanesi if structure ile === esit kullanarak compare mi yapiyoruz? == degil miydi o? 2.cisi ise php'de time() fonksiyonu ile klasor isminin cakismasi oldukca uzak bir olasilik gibi gorunuyor ki bunu onlemek icin mesela ip numarasini da isin icine katip daha sonra md5ledikten sonra ; belli bir kismini alip kullanmakta bu olasiligi kaldiriyor..

== olacak.

/* www.kefya.com */

üye olunpillinetwork sitelerine yorum ekleyebilmek ve daha fazlası için, üye olun ya da giriş yapın.

Bu yazıyı rapor et. Kural dışı içeriğe rastladığınızda editörlerimize rapor ederek müdahale edilmesini sağlayabilirsiniz. (Hangi durumlarda rapor edebilirim?)

Bu site

Nokta ve pilli ortak yapımı olan kodaman.org hep birlikte içerik üretip gelirini yazarları ile paylaştığımız kolektif bir kod yazarları blogudur. Siz de katılabilirsiniz.

son yorumlar

arama