JavaScript'te Parçalı Dosya Yüklemesi Nasıl Uygulanır
JavaScript'te sürdürülebilir parçalı dosya yüklemelerini uygulayın. Büyük dosyaları yönetin, ilerlemeyi takip edin ve ağ kesintilerinden kurtarın.
JavaScript'te parçalı dosya yüklemesi, büyük bir dosyayı sabit boyutlu parçalara (genellikle 5-10 MB) böler, her birini ayrı bir HTTP isteği olarak gönderir ve sunucuda yeniden birleştirir. Bu desen üç gerçek sorunu çözer: tarayıcılar ve proxy'ler 2 GB'ın üzerindeki istekleri keser, mobil ağlar yükleme ortasında bağlantıyı düşürür ve kullanıcılar ilerleme geri bildirimi ister. Çalışan bir uygulama; parçaları kesmek için File.slice(), parça başına AbortSignal ile fetch, S3 multipart veya özel birleştirici aracılığıyla sunucu tarafı birleştirme ve sekme yeniden yüklemelerinden sonra da sürdürülebilirliği sağlamak için IndexedDB'de yerel indeks kullanır.
Parçaların Tek Seferlik Yüklemelere Üstünlüğü
4 GB'lık bir dosyanın tek istek olarak yüklenmesi öngörülebilir nedenlerle başarısız olur: Nginx'in varsayılan client_max_body_size değeri 1 MB, Cloudflare ücretsiz katmanda istek başına 100 MB ile sınırlar, AWS API Gateway 10 MB'de kesinlikle durur ve mobil Safari, bellekte 4 GB'lık bir ArrayBuffer tutan sekmeleri sonlandırır. Parçalı yüklemeler bu tavanların tamamını aşar. Ayrıca gerçekten ilerleyen ilerleme çubukları, sıfırdan başlamayan yeniden denemeler ve duraklatma/devam ettirme özelliği elde edersiniz. Ödünü daha fazla sunucu tarafı durumu ve daha fazla gidiş-dönüştür — her 5 MB için yaklaşık bir HTTP isteği; 10 GB'lık bir dosya için bu 2.000 istek demektir.
Parça Boyutu Seçimi
Parça boyutu, aktarım hızı ile esneklik arasında bir dengedir. Çok küçük (1 MB'ın altında) olursa veri yerine TLS el sıkışmalarına daha fazla zaman harcanır. Çok büyük (100 MB'ın üzerinde) olursa düşen bir bağlantı dakikalarca yükleme israfına yol açar. Çoğu ağ için tatlı nokta 5-10 MB'dır; bu S3'ün 5 MB'lık multipart minimumuyla örtüşür ve tipik TCP pencere boyutlarıyla iyi uyum sağlar.
Önce kullanıcının ağını ölçün:
const downlink = navigator.connection?.downlink ?? 10;
const chunkSize = downlink > 20 ? 10 * 1024 * 1024 : 5 * 1024 * 1024;
100 Mbit bağlantıda 10 MB'lık parçalar her biri yaklaşık bir saniyede tamamlanır. 4G'de 5 MB'lık parçalar, tünel girdiğinizde daha iyi kurtarma sağlar.
Dosyayı Dilimleme ve Hash'leme
File.slice(), kopyalamadan aynı temel disk baytlarına başvuran bir Blob döndürür; bu nedenle 20 GB'lık bir dosyayı dilimlemek hiçbir şeye mal olmaz:
function* sliceFile(file, chunkSize) {
for (let offset = 0; offset < file.size; offset += chunkSize) {
yield {
index: Math.floor(offset / chunkSize),
blob: file.slice(offset, offset + chunkSize),
start: offset,
end: Math.min(offset + chunkSize, file.size)
};
}
}
Sunucunun bütünlüğü doğrulayabilmesi için yüklemeden önce her parçanın SHA-256 hash'ini hesaplayın:
const buffer = await chunk.blob.arrayBuffer();
const digest = await crypto.subtle.digest('SHA-256', buffer);
const hash = Array.from(new Uint8Array(digest))
.map(b => b.toString(16).padStart(2, '0')).join('');
10 GB'lık veri için hash hesaplama, modern bir dizüstü bilgisayarda yaklaşık 20 saniye ekler — kararsız mobil bağlantılarda sessiz bozulmayı yakalamak için buna değer.
Kontrollü Eş Zamanlılıkla Yükleme
Sıralı yüklemeler bant genişliğini israf eder; sınırsız paralellik tarayıcıyı çökertirir. 3-4 uçuş halindeki parçayla eş zamanlılık sınırı her ikisini dengeler:
async function uploadAll(file, sessionId) {
const queue = [...sliceFile(file, 5 * 1024 * 1024)];
const workers = Array.from({ length: 4 }, async () => {
while (queue.length) {
const chunk = queue.shift();
await uploadChunk(chunk, sessionId);
emitProgress(chunk.index);
}
});
await Promise.all(workers);
}
Her uploadChunk çağrısı, gövde olarak blob ve başlıkta hash ile bir PUT /upload/:sessionId/:index isteğidir. Tüm grubu sonlandırmadan tek tek istekleri iptal edebilmek için parça başına AbortController kullanın.
Sunucuyu Patlatmadan Yeniden Deneme
Ağ hataları dar döngü yeniden denemelerini değil, üstel geri çekilmeyi gerektirir. Makul bir politika: 3 deneme, 500 ms taban gecikme, %50'ye kadar jitter:
async function uploadChunk(chunk, sessionId, attempt = 0) {
try {
const res = await fetch(`/upload/${sessionId}/${chunk.index}`, {
method: 'PUT', body: chunk.blob, headers: { 'X-Hash': chunk.hash }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
} catch (e) {
if (attempt >= 3) throw e;
const delay = 500 * 2 ** attempt + Math.random() * 250;
await new Promise(r => setTimeout(r, delay));
return uploadChunk(chunk, sessionId, attempt + 1);
}
}
5xx yanıtlarını yeniden denenebilir, 4xx yanıtlarını öldürücü olarak ele alın (408 ve 429 hariç). 429'da yerel geri çekilmeniz yerine Retry-After başlığını uygulayın.
Sekme Yeniden Yükleme Sonrası Sürdürme
Yükleme durumunu her başarılı parçadan sonra IndexedDB'ye kaydedin:
await db.put('uploads', {
sessionId, fileName: file.name, fileSize: file.size,
completedChunks: [...completedSet], updatedAt: Date.now()
}, sessionId);
Kullanıcı aynı dosya seçiciyle sayfayı yeniden açtığında dosyanın size, lastModified ve adını saklanan oturumlarla karşılaştırın. Eşleşme varsa sunucuya hangi parçaları aldığını sorun (bir bitmap döndüren basit bir GET /upload/:sessionId/status çalışır), ardından yalnızca eksik olanları yükleyin. tus protokolü, Upload-Offset başlığıyla tam olarak bu deseni resmileştirir ve kendi uygulamanızı yazmak istemiyorsanız tus-js-client kütüphanesi sağlam bir uygulama sunar.
Sunucuda Parçaları Birleştirme
İki ciddi seçenek vardır: S3 multipart yükleme (her parça bir PartNumber olur ve son CompleteMultipartUpload bunları birleştirir) veya her parçayı geçici bir dosyaya yazan ve sonunda birleştiren özel bir birleştirici. S3 multipart, birleştirme sırasında hiçbir zaman çıkış için ödeme yapmamanız ve R2'nin sıfır çıkış okuma sunması nedeniyle ölçekte daha ucuzdur. Özel yaklaşım hata ayıklaması daha kolaydır ve birleştirme sırasında akış şifrelemeye olanak tanır.
S3 tarzı için:
const upload = await s3.createMultipartUpload({ Bucket, Key });
// parça başına: s3.uploadPart({ UploadId, PartNumber, Body })
await s3.completeMultipartUpload({ UploadId, MultipartUpload: { Parts } });
10.000 parça sınırına dikkat edin — 50 GB'ın üzerindeki dosyalar için bu sınırın altında kalmak adına 5 MB'ın üzerinde parçalar gereklidir.
Kullanıcıların Gerçekten Güvendiği İlerleme Takibi
Zıplayan ilerleme çubukları bozuk hissettirir. İlerlemeyi tamamlanan parçalara değil yüklenen baytların toplam bayta oranına göre hesaplayın ve titreşimi gizlemek için 2 saniyelik hareketli ortalama ile yumuşatın. HTTP/3'te XMLHttpRequestUpload.upload.onprogress her zaman güvenilir şekilde tetiklenmediğinden, bayt saymak için ReadableStream ve Transform ile fetch kullanın. Kalan baytları son aktarım hızına bölerek tahmini süreyi görüntüleyin, ancak "2 saniye kaldı... 10 dakikadır" deneyiminden kaçınmak için en az 5 saniyeye sınırlayın.
Yaygın Tuzaklardan Kaçınma
Üç hata parçalı yüklemeleri üretimde mahveder: parça başına Content-Length ayarlamayı unutmak (bazı uç proxy'leri bozar), farklı dosyalarda aynı oturum kimliğini yeniden kullanmak (birleştirmeyi bozar) ve oturumu sürümlendirmeden kullanıcının yükleme ortasında dosyayı değiştirmesine izin vermek. Oturumları parmak izi almak için her zaman dosyanın ilk 1 MB'ını, boyutunu ve lastModified değerini hash'leyin. Asla yalnızca lastModified değerine güvenmeyin — macOS Finder, meta veri değişikliklerinde onu günceller.
HexaTransfer bu tür parçalı ve sürdürülebilir bir pipeline'ı 10 GB'lık yüklemeleri için kaput altında kullanır; PUT'tan önce her parçaya istemci tarafı AES-256-GCM şifreleme eklenmiştir. hexatransfer.com'da deneyin — ücretsiz, hesap gerekmez, maksimum 10 GB.
Hepsini Bir Araya Getirme
Üretime hazır bir parçalı yükleyici yaklaşık 300 satır JavaScript'tir: File.slice ile dilimleme, SubtleCrypto ile hash'leme, üstel geri çekilmeyle 3-4 paralel parça yükleme, IndexedDB'ye oturum durumu kaydetme ve sunucunun S3 multipart veya özel birleştirici aracılığıyla parçaları birleştirmesi. Ona güvenmeden önce uçak modu geçişleri, sekme yeniden yüklemeleri ve 4G'de 15 GB'lık dosyayla test edin. Bu çalışınca şifreleme, ilerleme ve sürdürülebilirlik eklemek aynı iskelet üzerine eklenen katmanlardan ibarettir.
Uçtan uca şifreleme ile büyük dosyaları güvenle gönderin
Uçtan uca şifreleme ile 10 GB'a kadar dosya ücretsiz aktarın. Hesap gerekmez. Dosyalarınız yüklenmeden önce tarayıcınızda şifrelenir — başka kimse okuyamaz.
Dosya gönder