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>

Spacer
Spacer
/* Etiketler: , , , , */
/* pinkfloyd yazdı. 01 Ocak 2009 09:50. 5 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..

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 */

@its m00dy & alpr;

"===" ve "==" farklı karşılaştırmalardır. "==" sadece değerin eşitliğini karşılaştırırken, "===" veri türünü de karşılaştırır.

/* b+ */

ü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.

pilliilan

son yorumlar

arama

pillinetwork