Makale Özeti

FileUpload kontrolünün kullanımı ve alan şablonlarının çalışma mantığıyla özel özniteliklerin kullanımı hakkında bilgi içerir

Makale

MetaData üzerinden öznitelik ataması yapılarak doğrulama, biçimleme ve özel alan şanlonlarını(FieldTemplates) nasıl kullanacağımızdan bahsetmiştik.

Bu makale özel özniteliklerin ve alan şablonlarının geliştirilmesi konusunu içerecektir. Bu nedenle FileUpload kontrolünün kullanımı ve alan şablonlarının çalışma mantığıyla özel özniteliklerin kullanımı hakkında bilgi sahibi olacağız.

Önceki makalemde bahsettiğim gibi ASP.NET Dynamic Data Web Site ile FileUpload işlemlerini klasik yöntemlerle kullanamıyoruz.

ASP.NET Dynamic Data Web Sitesinde Ajax Toolkit ile birlikte kullanıyorsanız ve FileUpload bileşeniniz UpdatePanel içerisinde yer alıyorsa ScriptManager nesnesinin EnablePartialRendering özelliğini false olarak atamanız gerekiyor.


Kodlamaya başlamadan önce yapılması gereken işlemleri özetlemenin faydalı olduğunu düşünüyorum. Kodlama detayları ilgili sınıfların içinde yer almaktadır.

  1. Zorunlu olmasa da dosyanın sunucuya yükleme işleminin gerçekleştirileceği yardımcı bir sınıf.
  2. Her bir dosya yükleme alanı için dosya türü, boyutu vb. bildirimleri yapmak için öznitelik tabanlı bir sınıf
  3. Dosya yükleme alanlarının ekleme ve güncelleme ekranı için FileUpload kontrolü listeleme ve detay sayfaları için ise dosya türüne göre yüklenen dosyaya uygun simge, önizleme vb. için alan şablonlarının(FieldTemplate) gerekli hazırlanması.
  4. İşlevin kazandırılacağı alanlara gerekli öznitelik tanımlamasının yapılması

Temel düzeyde dosya yükleme işlemini gerçekleştirmek üzere aşağıdaki sınıfı kullanacağız

FileUploadUtil.cs

using System;
using System.Web;
using System.IO;
using Eposta.Core.CustomAttributes;
using Eposta.Core.Extentions;
using System.Text.RegularExpressions;

namespace Eposta.Core.Utils
{
    /// <summary>
    /// Ömer Faruk ZORLU
    /// </summary>
    public class FileUploadUtil
    {
        private string _fileName;

        /// <summary>
        /// Dosyanın yükleneceği fiziksel konum
        /// Örnek kullanım: HttpContext.Current.Server.MapPath("~/Uploads/")
        /// </summary>
        public string UploadDictionaryPath { get; set; }

        /// <summary>
        /// Yükleme işlemi başarısız olmuşsa bu değer 
        /// üzerinden hata mesajı okunabilir
        /// </summary>
        public string ErrorMessage { get; private set; }


        /// <summary>
        /// Değer atama işleminin ardından güvenlik, tekillik gibi
        /// nedenler gereği isim değiştirilmelidir. 
        /// </summary>
        public string FileName
        {
            get { return _fileName; }
            set
            {
                string strExtention = Path.GetExtension(value);

                // ToSEFString methodu gönderilen string değeri arama 
                // motoru dostu karakterler ile değiştirir
                //
                // RandomHelper sınıfı doğru rasgele karakterler ve 
                // sayılar üretmek için kullanılıyor
                // Bağlantı: http://tinyurl.com/y8of6sn
                _fileName = Path.GetFileNameWithoutExtension(value).ToSEFString()
                + RandomHelper.RandomString(10, true)
                + strExtention;
            }
        }

        /// <summary>
        /// ctor
        /// </summary>
        public FileUploadUtil()
        {
            _fileName = string.Empty;
        }

        /// <summary>
        /// Dosya yükleme işleminin tetiklenmesi için bu method çağırılmalıdır.
        /// </summary>
        /// <param name="postedFile">
        /// Bu değişken klasik html input alanları tarafından post edilen 
        /// verilerinde methodu kullanabilmeleri için bu tipte tanımlanmıştır.
        /// FileUpload kontrolü kullanılacaksa PostedFile özelliği gönderilebilir.
        /// </param>
        /// <param name="fuAttribute">
        /// Dosya yükleme işleminin gerçekleşmesi ile ilgili bilgiler içeren sınıftır
        /// Attribute sınıfından türetilmiştir. MetaColumn ile kullanılacaksa ilgili
        /// alan üzerinde tanımlı özniteliğin gönderilmesi yeterlidir.
        /// </param>
        /// <returns></returns>
        public bool UploadFile(HttpPostedFile postedFile, FileUploadAttribute fuAttribute)
        {
            #region Checks
            if (postedFile.ContentLength <= 0)
            {
                this.ErrorMessage = "Veri akışı bulunamadı";
                return false;
            }

            // Maksimum dosya boyutu öznitelikte atanan değere göre kontrol ediliyor.
            long FileSize = postedFile.ContentLength;
            if (FileSize > (fuAttribute.MaximumFileSize * 1024000))
            {
                this.ErrorMessage = string.Format("Maksimum dosya boyutu izin verilenden fazla olamaz. İzin verilen maksimum dosya boyutu: {0}", fuAttribute.MaximumFileSize);
                return false;
            }

            // Dosya uzantısı öznitelikte atanan değere göre kontrol ediliyor.
            // İçerik türüne(MIME) görede kontrol edilebilir.
            if (!Regex.IsMatch(Path.GetExtension(_fileName), fuAttribute.AllowedFileFormats, RegexOptions.IgnoreCase))
            {
                this.ErrorMessage = string.Format("Dosya türüne izin verilmiyor. İzin verilen dosya formatları: {0}", fuAttribute.AllowedFileFormats);
                return false;
            }
            #endregion

            // Klasör yoksa oluştur.
            DirectoryInfo DInfo = new DirectoryInfo(this.UploadDictionaryPath);
            if (!DInfo.Exists)
                DInfo.Create();

            #region WriteStreamToFile
            // Dosyayı kaydet
            string strFullFileName = this.UploadDictionaryPath + this._fileName;
            postedFile.SaveAs(strFullFileName);
            #endregion

            return true;
        }
    }
}


Yukarıdaki sınıf içinde kullanılan FileUploadAttribute sınıfını dosya yükleme işleminin gerçekleşmesini istediğimiz kolon için gerekli bildirimlerin yapılması için kullanacağız . Bu sınıfın kodlaması aşağıdaki gibidir.

using System;

namespace Eposta.Core.CustomAttributes
{
    /// <summary>
    /// Ömer Faruk ZORLU
    /// </summary>
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
    public class FileUploadAttribute : Attribute
    {
        /// <summary>
        /// Yüklenecek dosyanın maksimum boyutunun megabyte karşılığıdır.
        /// </summary>
        public short MaximumFileSize { get; set; }

        /// <summary>
        /// Bu özellik adının kontrol edilmesi için kullanılır.
        /// Kontrol işlemi RegularExpressions(Regex) sınıfı ile
        /// gerçekleştirilir.
        ///
        /// Örnek Kullanım: "jpg|gif|png|JPG|GIF|PNG"
        /// </summary>
        public string AllowedFileFormats { get; set; }

        /// <summary>
        /// Yükleme işleminin gerçekleştirileceği klasörün fiziksel
        /// yolunu saklar.
        ///
        /// Örnek Kullanım: HttpContext.Current.Server.MapPath("~/Uploads/")
        /// </summary>
        public string UploadDictionaryPath { get; set; }
    }
}


Buraya kadarki kodlamalarla iş katmanı için yükleme işini üstlenecek sınıfların kodlamasını tamamlamış olduk. Sırada bu katmanı istediğimiz kolon üzerinde kullanabilmek için gerekli alan şablonunu tasarlama işi var.  Daha önceki makalelerde belirttiğim gibi güncelleme, ekleme ve listeleme, detay sayfa şablonları için iki adet alan şablonu hazırlanmalıdır. Ekleme ve günnceleme için gerekli alan şablonunun kodları aşağıdaki gibidir.

MyFileUpload_Edit.ascx

<%@ Control Language="C#" CodeFile="MyUpload_Edit.ascx.cs" Inherits="MyUpload_Edit" %>
<asp:Label ID="Label1" runat="server" Text='<%# FieldValueEditString %>'></asp:Label><br />
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:CustomValidator ID="CustomValidator1" runat="server" 
    ErrorMessage="CustomValidator"></asp:CustomValidator>



MyFileUpload_Edit.ascx.cs

using System;
using System.Collections.Specialized;
using System.Web.UI;
using System.Linq;
using Eposta.Core.CustomAttributes;
using Eposta.Core.Utils;

public partial class MyUpload_Edit : System.Web.DynamicData.FieldTemplateUserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected override void OnDataBinding(EventArgs e)
    {
        base.OnDataBinding(e);
    }

    protected override void ExtractValues(IOrderedDictionary dictionary)
    {
        // DataControl nesnesi olarak Label1 atandığı için
        // dosya yüklemenmesede varsayılan olarak zaten kayıtlı olan
        // veri dönüş değerinde kullanılmak üzere atanıyor
        dictionary[Column.Name] = ConvertEditedValue(Label1.Text);

        // Bu kolon üzerinde atanması zorunlu olan FileUploadAttribute
        // özniteliği alınıyor.
        var fuAttribute = base.Column.Attributes.OfType<FileUploadAttribute>().FirstOrDefault();
        // FileUploadAttribute ataması yoksa dön
        if (fuAttribute == null)
            return;

        // Dosya seçilmişse
        if (FileUpload1.HasFile)
        {
            try
            {
                FileUploadUtil fuUtil = new FileUploadUtil();
                // Atama işleminin ardından dosyaya yeni isim atanıyor.
                fuUtil.FileName = FileUpload1.FileName;
                fuUtil.UploadDictionaryPath = fuAttribute.UploadDictionaryPath;
                
                // Dosyayı yükle ve yeni dosya adını dönüş değeri olarak ata.
                // Hata kontrolüne karşı ilgili değişken(string ErrorMessage)
                // kontrol edilebilir.
                if (fuUtil.UploadFile(FileUpload1.PostedFile, fuAttribute))
                    dictionary[Column.Name] = fuUtil.FileName;
            }
            catch (Exception Ex)
            {
                // Denetlenmemiş herhangi bir hata oluşursa sayfanın tamamen 
                // hata vermesi yerine hata mesajını hata özetlerinde ve atama
                // işleminin yapıldığı alanın yanında görünmesi için CustomValidator1
                // nesnesinden yararlanılıyor.
                CustomValidator1.IsValid = false;
                CustomValidator1.ErrorMessage = Ex.Message;
            }
        }
    }

    public override Control DataControl
    {
        get
        {
            return Label1;
        }
    }
}


Listeleme ve detay sayfa şablonlarında kullanılacak alan şablonu kodları aşağıdaki gibidir. Yüklenen dosyaların(ilgili verilerin) yalnızca    resim dosyası olduğu öngörülerek yazılmıştır. Dosya tipi yada türüne göre özelleştirilmiş görüntüleme tekniği kullanılabilir.


MyUpload.ascx.cs

using System;
using System.Web.UI;

public partial class MyUpload : System.Web.DynamicData.FieldTemplateUserControl
{
    public override Control DataControl
    {
        get
        {
            return imgThumbnail;
        }
    }

    public string GetSrcUrl()
    {
        // Veri boş ya da null ise NoImage.gif dönder
        if (String.IsNullOrEmpty(FieldValueString)) return "NoImage.gif";

        // Veri http, ftp vb herhangi bir protokolü kullanıyor ise
        // olduğu gibi değilse upload klasörünü kök dizin alarak dönder 
        if (-1 < FieldValueString.IndexOf("://"))
            return FieldValueString;
        else
            return "~/uploads/" + FieldValueString;
    }
} 

Dynamic Data’nın en sevdiğim kısımı tabiki MetaData üzerinde yapılan tanımlamalardır. O kadar çok kod yazmak gerekiyorki daha ne yapalım birde diyenleri duyar gibiyim.

Dynamic Data ile yapılan her geliştirme tasarruf niteliği taşır.


Geliştirdiğimiz dosya yükleme aracı için klasik kodlama kullansaydık benzer kodları kullanmak istediğimiz sayfalarda(aynı sayfada pek çok kere de olabilir) harcanan efor göz önüne alınmalıdır. Yapılan geliştirme bir sonraki proje için yatırımdır. Ayrıca geriye dönük olarak yapılan projelerde kullan/a/mamak için bir neden söz konusu değildir.

Şimdi en zevkli kısıma gelelim. Bundan böyle dosya yükleme(FileUpload) işlemi ile çalışmasını istediğiniz herhangi bir kolon için meta data üzerinde aşağıdaki gibi bir tanımlama yapmanız yeterli oluyor.

NORTWIND veritabanında bulunan Employee tablosundaki PhotoPath kolonu için yapılan örnek tanımlama:

using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(EmployeeMetaData))]
public partial class Employee
{

}

public class EmployeeMetaData
{
    [Eposta.Core.CustomAttributes.FileUpload(
        AllowedFileFormats = "jpg|gif|png|JPG|GIF|PNG",
        MaximumFileSize = 1, // MB
        UploadDictionaryPath = "~/uploads/"
        )]
    [UIHint("MyUpload")]
    public string PhotoPath { get; set; }
} 


Ekran görüntüleri
Listemele ve detay sayfa şablonlarında:




Ekleme ve Güncelleme sayfa şablonlarında:

 

Meraklısından meraklılarına faydalı olması dileğiyle. Mutlu kodlar.

Ömer Faruk ZORLU