REST API - dosya (resim) işleme - en iyi uygulamalar


198

JSON'u kabul eden ve yanıtlayan REST API ile sunucu geliştiriyoruz. Sorun, görüntüleri istemciden sunucuya yüklemeniz gerekiyorsa.

Not: ve ayrıca varlığın (kullanıcı) birden fazla dosyaya (carPhoto, licensePhoto) sahip olabileceği ve başka özelliklere (ad, e-posta ...) sahip olabileceği bir kullanım durumundan bahsediyorum, ancak yeni kullanıcı oluşturduğunuzda, Bu resimleri göndermeyin, kayıt işleminden sonra eklenirler.


Farkında olduğum çözümler, ancak her birinin bazı kusurları var

1. JSON yerine çoklu bölüm / form verileri kullanın

iyi : POST ve PUT istekleri mümkün olduğunca RESTful'dur, dosya ile birlikte metin girişleri içerebilirler.

eksileri : Artık JSON değil, test etmek, hata ayıklamak vb. çok daha kolay.

2. Ayrı dosyaları güncellemeye izin ver

Yeni kullanıcı oluşturmak için POST isteği, resim eklemeye izin vermiyor (başlangıçta söylediğim gibi bizim durumumuzda sorun yok), resim yükleme, örneğin / users / 4 / carPhoto'a çoklu bölüm / form verileri olarak PUT isteği ile yapılır.

iyi : Her şey (dosya yükleme kendisi hariç) JSON'da kalır, test edilmesi ve hata ayıklaması kolaydır (tam JSON isteklerini uzunluklarından korkmadan günlüğe kaydedebilirsiniz)

eksileri : Sezgisel değil, POST veya PUT tüm varlık değişkenlerini aynı anda /users/4/carPhotoyapamazsınız ve ayrıca bu adres bir koleksiyon olarak daha fazla düşünülebilir (REST API için standart kullanım durumu şuna benzer /users/4/shipments). Genellikle varlığın her bir değişkenini GET / PUT, örneğin kullananlar / 4 / name gibi edemezsiniz (ve istemezsiniz). GET ile isim alabilir ve PUT ile kullanıcıları değiştirebilirsiniz / 4. Kimlikten sonra bir şey varsa, genellikle kullanıcılar / 4 / yorumlar gibi başka bir koleksiyon

3. Base64 kullanın

JSON olarak gönderin, ancak Base64 ile dosyaları kodlayın.

iyi : İlk çözümle aynı, mümkün olduğunca RESTful hizmetidir.

eksileri : Bir kez daha, test ve hata ayıklama çok daha kötü (vücudun megabayt veri olabilir), hem boyut hem de işlem süresinde artış var - hem istemci hem de sunucu


Gerçekten çözüm no kullanmak istiyorum. 2, ama eksileri vardır ... Herkes bana "en iyi nedir" çözüm daha iyi bir fikir verebilir?

Amacım mümkün olduğunca basit tutmak isterken mümkün olduğunca çok standart içeren RESTful hizmetlerine sahip olmak.


Bunu da yararlı bulabilirsiniz: stackoverflow.com/questions/4083702/…
Markon

5
Bu konunun eski olduğunu biliyorum, ancak son zamanlarda bu sorunla karşılaştık. Sahip olduğumuz en iyi yaklaşım, 2 numaralı numaranıza benzer. Dosyaları doğrudan API'ye yükler ve daha sonra bu dosyaları modele ekleriz. Bu senaryo ile formdan önce, sonra veya aynı sayfada yükleme görüntüleri oluşturabilirsiniz, gerçekten önemli değil. İyi tartışma!
Tiago Matos

2
@TiagoMatos - evet, tam olarak, son zamanlarda kabul ettiğim bir cevapta tarif ettim
libik

6
Bu soruyu sorduğun için teşekkürler.
Zuhayer Tahir

1
"ayrıca bu adres / kullanıcılar / 4 / carPhoto daha çok bir koleksiyon olarak kabul edilebilir" - hayır, bir koleksiyona benzemez ve mutlaka bir koleksiyon olarak kabul edilmez. Bir koleksiyon değil, tek bir kaynak olan bir kaynakla ilişkiniz olması gayet iyi.
B12 Tost Makinesi

Yanıtlar:


156

OP burada (Bu soruyu iki yıl sonra cevaplıyorum, Daniel Cerecedo tarafından yapılan yazı bir anda kötü değildi, ancak web hizmetleri çok hızlı gelişiyor)

Üç yıllık tam zamanlı yazılım geliştirmelerinden sonra (yazılım mimarisi, proje yönetimi ve mikro hizmet mimarisine de odaklanarak) kesinlikle en iyisi olarak ikinci yolu (ancak bir genel son nokta ile) seçiyorum.

Görüntüler için özel bir uç noktanız varsa, bu görüntüleri işlemek için size çok daha fazla güç verir.

Hem mobil uygulamalar (iOS / android) hem de ön uç (React kullanarak) için aynı REST API'sına (Node.js) sahibiz. Bu 2017, bu nedenle görüntüleri yerel olarak saklamak istemiyorsunuz, onları bir miktar bulut depolama alanına (Google bulut, s3, bulut, ...) yüklemek istiyorsunuz, bu nedenle üzerlerinde genel bir işlem yapmak istiyorsunuz.

Tipik akışımız, bir resim seçer seçmez, arka plana (genellikle POST açık / resim bitiş noktası) yüklenmeye başlaması ve yükledikten sonra kimliği döndürmesidir. Bu gerçekten kullanıcı dostudur, çünkü kullanıcı bir görüntü seçer ve daha sonra diğer alanlarla (örneğin adres, ad, ...) devam eder, bu nedenle "gönder" düğmesine bastığında, görüntü genellikle zaten yüklenir. Beklemiyor ve "yükleniyor ..." diyerek ekranı izlemiyor.

Aynı şey görüntü almak için de geçerlidir. Özellikle cep telefonları ve sınırlı mobil veriler sayesinde, orijinal görüntüler göndermek istemezsiniz, yeniden boyutlandırılmış görüntüler göndermek istersiniz, böylece çok fazla bant genişliği almazlar (ve mobil uygulamalarınızı daha hızlı hale getirmek için, genellikle istemezsiniz) yeniden boyutlandırmak için görünümünüze mükemmel şekilde uyan görüntüyü istersiniz). Bu nedenle, iyi uygulamalar bulut gibi bir şey kullanıyor (veya yeniden boyutlandırmak için kendi görüntü sunucumuz var).

Ayrıca, veriler özel değilse, o zaman app / frontend sadece URL'ye geri gönderirsiniz ve doğrudan bulut depolama alanından indirirsiniz, bu da sunucunuz için bant genişliğinden ve işlem süresinden büyük tasarruf sağlar. Daha büyük uygulamalarımızda her ay çok fazla terabayt indirilir, bunu doğrudan CRUD işlemine odaklanan REST API sunucunuzun her birinde ele almak istemezsiniz. Bunu tek bir yerde (önbellekleme özelliği olan Imageserver'ımız) ele almak veya bulut hizmetlerinin tümünü halletmesine izin vermek istersiniz.


Eksileri: Düşünmeniz gereken tek "eksileri" "atanmamış resimler" dir. Kullanıcı görüntüleri seçer ve diğer alanları doldurmaya devam eder, ancak "nah" der ve uygulamayı veya sekmeyi kapatır, ancak bu arada görüntüyü başarıyla yüklediniz. Bu, hiçbir yere atanmamış bir resim yüklediğiniz anlamına gelir.

Bunu kullanmanın birkaç yolu vardır. En kolay olanı "umrumda değil", ki bu çok sık gerçekleşmiyorsa veya hatta size gönderen her görüntüyü (herhangi bir nedenle) saklamak istiyorsanız ve herhangi bir şey istemiyorsanız, ilgili olanıdır. silme.

Bir diğeri de kolaydır - CRON'unuz var, yani her hafta ve bir haftadan eski atanmamış tüm görüntüleri siliyorsunuz.


[Görüntüyü seçer seçmez, istek internet bağlantısı nedeniyle başarısız olduğunda arka plana (genellikle POST açık / resim bitiş noktası) yüklenmeye başlarsa, yükledikten sonra kimliği döndürürse ne olur? Kullanıcıya başka alanlarla (adres, ad, ...) devam ederken bilgi verir misiniz? Eminim hala kullanıcı "gönder" butonuna basana kadar bekleyeceksiniz ve "uploadiing" diyen ekranı izlerken onları bekletmeyi tekrar deneyin.
Adromil Balais

5
@AdromilBalais - RESTful API vatansızdır, bu nedenle hiçbir şey yapmaz (Sunucu tüketicinin durumunu izlemez). Hizmet tüketicisi (yani web sayfası veya mobil cihaz) başarısız istekleri işlemekten sorumludur, bu nedenle tüketici bu istek başarısız olduktan hemen sonra aynı isteği çağırıp çağırmayacağına veya ne yapılacağına karar vermelidir (ör. "Resim yükleme başarısız - tekrar denemek istiyorum) ")
libik

2
Çok bilgilendirici ve aydınlatıcı bir cevap. Cevapladığınız için teşekkürler.
Zuhayer Tahir

Bu ilk sorunu gerçekten çözmez. Bu sadece "bir bulut hizmeti kullanın" diyor
Martin Muzatko

3
@MartinMuzatko - yapar, ikinci seçeneği seçer ve nasıl kullanmanız gerektiğini ve nedenini söyler. "Ama bu, her şeyi tek bir talepte ve ima etmeden göndermenizi sağlayan mükemmel bir seçenek değil" - evet, ne yazık ki böyle bir çözüm yok.
libik

105

Verilecek birkaç karar var :

  1. Kaynak yolu hakkında ilk :

    • Görüntüyü kendi başına bir kaynak olarak modelleyin:

      • Yuvaya yerleştirilmiş kullanıcı (/ user /: id / image): kullanıcı ve resim arasındaki ilişki örtülü olarak yapılır

      • Kök yolunda (/ image):

        • Müşteri, görüntü ve kullanıcı arasındaki ilişkiyi kurmaktan sorumlu tutulur veya;

        • Bir görüntü oluşturmak için kullanılan POST isteği ile bir güvenlik bağlamı sağlanıyorsa, sunucu kimliği doğrulanmış kullanıcı ile görüntü arasında dolaylı olarak bir ilişki kurabilir.

    • Görüntüyü kullanıcının bir parçası olarak gömme

  2. İkinci karar, görüntü kaynağının nasıl temsil edileceğiyle ilgilidir :

    • Base 64 kodlu JSON yükü olarak
    • Çok parçalı yük olarak

Bu benim karar yolum olurdu:

  • Genellikle güçlü bir durum olmadığı sürece performanstan ziyade tasarımı tercih ederim. Sistemi daha bakımlı hale getirir ve entegratörler tarafından daha kolay anlaşılabilir.
  • Bu yüzden ilk düşüncem, resim kaynağının Base64 temsilini almaktır çünkü her şeyi JSON'da tutmanıza izin verir. Bu seçeneği belirlediyseniz, kaynak yolunu istediğiniz gibi modelleyebilirsiniz.
    • Eğer kullanıcı ve resim arasındaki ilişki 1'e 1 ise, her iki veri seti de aynı anda güncellenirse, görüntüyü bir özellik olarak modellemeyi tercih ederim. Başka bir durumda, görüntüyü bir nitelik olarak, PUT veya PATCH ile güncelleyerek veya ayrı bir kaynak olarak özgürce modellemeyi seçebilirsiniz.
  • Çok parçalı yükü seçerseniz, görüntüyü bir kaynak olarak modellemeye mecbur hissederim, böylece bizim durumumuzda kullanıcı kaynağı, görüntü için ikili gösterim kullanma kararından etkilenmez.

Sonra şu soru geliyor: base64 vs multipart seçiminde herhangi bir performans etkisi var mı? . Çok parçalı formatta veri alışverişinin daha verimli olması gerektiğini düşünebiliriz. Ancak bu makale , her iki temsilin boyut açısından ne kadar az farklılık gösterdiğini göstermektedir.

Seçimim Base64:

  • Tutarlı tasarım kararı
  • İhmal edilebilir performans etkisi
  • Tarayıcılar veri URI'lerini (base64 kodlu resimler) anladığından, istemci bir tarayıcıysa bunları dönüştürmeye gerek yoktur
  • Bir öznitelik veya bağımsız bir kaynak olup olmadığı konusunda oy vermeyeceğim, sorun alanınıza (bilmediğim) ve kişisel tercihinize bağlıdır.

3
Verileri protobuf vb. Gibi diğer serileştirme protokollerini kullanarak kodlayamıyor muyuz? Temelde base64 kodlama ile birlikte gelen boyut ve işlem süresi artışını ele almak için başka basit yollar olup olmadığını anlamaya çalışıyorum.
Andy Dufresne

1
Çok ilgi çekici bir cevap. adım adım yaklaşım için teşekkürler. Bu beni daha iyi anlamamı sağladı.
Zuhayer Tahir

13

İkinci çözümünüz muhtemelen en doğru çözümdür. HTTP spesifikasyonunu ve mime türlerini amaçlandığı gibi kullanmalı ve dosyayı üzerinden yüklemelisiniz multipart/form-data. İlişkileri idare ederken, bu işlemi kullanırım (varsayımlarınız veya sistem tasarımınız hakkında sıfır olduğunu unutmayın):

  1. POSTiçin /userskullanıcı varlık yaratmak.
  2. POSTgörüntü için /images, emin bir dönüş Locationgörüntü HTTP Spec başına alınabilir yere başlığını.
  3. PATCHiçin /users/carPhotoverilen fotoğrafın kimliği ve ona atama Locationadım 2'de başlığına.

1
"İstemci API'yı nasıl kullanacak" konusunda doğrudan bir kontrole sahip değilim ... Sorun şu ki, bazı kaynaklara yama yapılmayan "ölü" resimler ...
libik

4
Genellikle ikinci seçeneği seçtiğinizde, önce ortam öğesini yüklemek ve ortam tanımlayıcısını istemciye döndürmek tercih edilir, daha sonra istemci ortam tanımlayıcısı dahil olmak üzere varlık verilerini gönderebilir, bu yaklaşım bozuk varlıklar veya uyumsuzluk bilgilerini önler.
Kellerman Rivero

2

Kolay bir çözüm yok. Her yolun artıları ve eksileri vardır. Ama kurallı yolu ilk seçeneği kullanıyor: multipart/form-data. As W3 öneri kılavuzu diyor

"Çok bölümlü / form verisi" içerik türü, dosyaları, ASCII olmayan verileri ve ikili verileri içeren formları göndermek için kullanılmalıdır.

Gerçekten form göndermiyoruz, ancak örtük prensip hala geçerli. Base64'i ikili gösterim olarak kullanmak yanlıştır, çünkü hedefinizi gerçekleştirmek için yanlış aracı kullanıyorsunuz, diğer yandan ikinci seçenek API istemcilerinizi API hizmetinizi tüketmek için daha fazla iş yapmaya zorlar. Tüketimi kolay bir API sağlamak için sıkı çalışmayı sunucu tarafında yapmanız gerekir. İlk seçenek hata ayıklamak kolay değildir, ancak bunu yaptığınızda muhtemelen asla değişmez.

Kullanılması multipart/form-datasizi DİNLENME / http felsefesi ile yapışabilir ediyoruz. Benzer bir sorunun cevabını burada görüntüleyebilirsiniz .

Alternatifleri karıştırırken başka bir seçenek de, çoklu bölüm / form verilerini kullanabilirsiniz, ancak her değeri ayrı ayrı göndermek yerine, içindeki json yükü ile faydalı yük adlı bir değer gönderebilirsiniz. (ASP.NET WebAPI 2 kullanarak bu yaklaşımı denedim ve iyi çalışıyor).


2
Bu W3 öneri kılavuzu, HTML 4 spesifikasyonu bağlamında olduğu için burada önemsizdir.
Johann

1
Çok doğru .... "ASCII olmayan veri" çok parçalı gerektirir? Yirmi birinci yüzyılda mı? UTF-8 dünyasında mı? Tabii ki bu bugün için saçma bir öneri. HTML 4 gün içinde var olmasına bile şaşırdım, ancak bazen İnternet altyapı dünyası çok yavaş ilerliyor.
Ray Toal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.