Flask İle Form Dosya Yükleme (File Upload) İşlemleri

Form yapılarının belki de en işlevsel elemanı olan dosya yükleme seçeneklerinin Flask ile ne kadar kolay kullanabildiğini göreceğiz. Bu sayede sayfanızdaki formlar ile kullanıcıların dosya yükleme işlemlerini rahatlıkla yapabileceksiniz.

Bir önceki yazımız olan Flask POST ve GET Form Kullanımı ile nasıl formlar oluşturabileceğimizi ve POST/GET farketmeksizin kullanıcılardan verilen bilgileri alabileceğimizi öğrendik. Şimdi ise geçen derste öğrendiğimiz form işlemlerine yeni bir özellik daha ekleyerek dosya yükleme (file upload) işlemlerini öğreneceğiz. Öyleyse hemen başlayarak adım adım ilerleyelim.

1. Form Sayfasının Oluşturulması

İlk olarak yapmamız gereken şey dosyayukleme.html isimli sayfamızı oluşturarak formumuza dosya yükleme butonunu koyalım ve form tipimizi enctype=multipart/form-data olarak belirleyelim. Ek olarakta sayfamızın içine flash mesajlarımızı ve resim yüklendikten sonra yüklenen resimi göstereceğimiz alanlar ekleyelim. Ardından da formumuzu POST edelim.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Dosya Yükleme</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

    {% with mesajlar = get_flashed_messages() %}
        {% if mesajlar %}
          <ul class=flashes>
          {% for mesaj in mesajlar %}
            <li>{{ mesaj }}</li>
          {% endfor %}
          </ul>
        {% endif %}
      {% endwith %}

    <form action="/dosyayukle" method="post" enctype=multipart/form-data>    
        <input type="file" name="dosya">
        <input type="submit" value="Gönder">
    </form>

    {% if dosya %}
        <br>
        <img width="250px" src="{{ url_for('static', filename='yuklemeler/'+ dosya) }}" />
    {% endif %}

</body>
</html>

2. Gerekli Kütüphanelerin ve Configlerin Eklenmesi

Flask ve dosya yükleme bileşenlerimizin çalışabilmesi için kütüphaleri sayfanın en üstünde dahil etmeyi unutmayın. Ardından dosya yolu,izin verilen uzantılar gibi ilk değişken tanımlamalarımızı da yapalım. İlk olarak kütüphaneler:

import os
from flask import Flask, render_template, request, redirect, abort, flash, url_for
from werkzeug.utils import secure_filename

Ardından basit tanımlarımızı halledelim:

YUKLEME_KLASORU = 'static/yuklemeler'
UZANTILAR = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

Ve şimdi Flask başlayabilir. Tabi:

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = YUKLEME_KLASORU
app.secret_key = "Flask_Dosya_Yukleme_Ornegi"

3. Güvenlik Kontrolleri

Formlardan neler yükleneceğini bilemeyiz. Bunun için ilk olarak güvenlik önlemlerimizi almak bize fayda sağlayacaktır. İlk güvenlik önlemimiz olan dosya uzantısı kontrolü ise basit ve önemli bir önlemdir. Çünkü saldırganlar .php veya .exe uzantılı gibi sunucu tarafından çalıştırılabilen dosyalarla kendi zararlı kodlarını sunucumuzda aktif edebilirler. Öyleyse sayfanın en başında eklediğimiz UZANTILAR isimli dizimiz ile gelen dosyanın bu uzantılardan birine sahip olup olmadığını kontrol edelim:

def uzanti_kontrol(dosyaadi):
   return '.' in dosyaadi and \
   dosyaadi.rsplit('.', 1)[1].lower() in UZANTILAR

Peki ne yaptık? İlk olarak uzanti_kontrol() isimli fonksiyonumuz ile dosyaadi isimli bir parametre aldık ve önce bu dosya adı bir uzantıya sahip mi diye (içinde . varsa uzantıya sahiptir), ardından da uzantısı bizim izin verdiğimiz dosya uzantılarından biri mi diye kontrol ettik. Eğer bu iki testten başarıyla geçerse True, geçemezse False dönecektir.

Şimdi ise secure_filename() fonksiyonu ile dosyanın sahip olduğu isim bizim için bir tehdit içeriyor mu diye kontrol edeceğiz. Nasıl yani derseniz diye hemen küçük bir örnek verelim. Diyelim ki saldırganımız sitemizin temel dosyaları olan resimleri vs değiştirmek istiyor. Site logomuzu, yazı resimlerini kendi resimleriyle değiştirmek istiyor ve bunu yapmak içinde dosya adını “../../../../home/images/logo.png” gibi bir formatta hazırladı ve dosya yükleme işlemini gerçekleştirdi. Sonuç olarak saldırgan bizim izin verdiğimiz klasöre değil, kendi istediği klasöre yüklemiş olduğu dosyasını. Tabi bu her sistemde çalışmayabilir ama emin olun o talihsiz siz olmak istemezsiniz. Bu nedenle şu şekilde küçük ama etkili bir kontrol mekanizması ekleyelim:

dosyaadi = secure_filename(dosya.filename)

!! Bu sayede “../../../../home/images/logo.png” şeklindeki bir dosya isminin yeni adı “home_images_logo.png” olacaktır.

4. Route İle Formdan Gelen Dosyayı Alma

Basit bir form sayfasını oluşturduktan sonra verileri action="/dosyayukle" etiketi ile /dosyayukle sayfasına post edeceğimizi belirtmiş olduk. Öyleyse hemen ilgili route u oluşturalım ve daha öncesinde yazdığımız güvenlik önlemlerini dahil edelim:

# Form ile dosya yükleme işlemi
@app.route('/dosyayukle', methods=['POST'])
def dosyayukle():

   if request.method == 'POST':

        # formdan dosya gelip gelmediğini kontrol edelim
      if 'dosya' not in request.files:
         flash('Dosya seçilmedi')
         return redirect('dosyayukleme')         

        # kullanıcı dosya seçmemiş ve tarayıcı boş isim göndermiş mi
      dosya = request.files['dosya']                    
      if dosya.filename == '':
         flash('Dosya seçilmedi')
         return redirect('dosyayukleme')

        # gelen dosyayı güvenlik önlemlerinden geçir
      if dosya and uzanti_kontrol(dosya.filename):
         dosyaadi = secure_filename(dosya.filename)
         dosya.save(os.path.join(app.config['UPLOAD_FOLDER'], dosyaadi))
         #return redirect(url_for('dosyayukleme',dosya=dosyaadi))
         return redirect('dosyayukleme/' + dosyaadi)
      else:
         flash('İzin verilmeyen dosya uzantısı')
         return redirect('dosyayukleme')

   else:
      abort(401)

Sonrada form sayfalarımızı oluşturduğumuz sayfaların route kısımlarını yazalım:

# Form ile dosya yükleme sayfası
@app.route('/dosyayukleme')
def dosyayukleme():
   return render_template("dosyayukleme.html")


# Form ile dosya yükleme sayfası - Sonuç
@app.route('/dosyayukleme/<string:dosya>')
def dosyayuklemesonuc(dosya):
   return render_template("dosyayukleme.html", dosya=dosya)

5. Sonuç

Artık form yapımız ile rahatlıkla dosya yükleme işlemini gerçekleştirebiliyoruz. Ağaşıdaki resimde çalışan örneklerini görebilir ve tüm kodları indirmek için bağlantıyı kullanabilirsiniz. Ayrıca aklınıza takılan sorular için yorum kısmını kullanabilirsiniz.

Projenin bitmiş halini indirmek için Tıklayınız

Resimler: