ดาวน์โหลดโมเดล AI ด้วย Background Fetch API

เผยแพร่: 20 กุมภาพันธ์ 2025

การดาวน์โหลดโมเดล AI ขนาดใหญ่อย่างน่าเชื่อถือเป็นงานที่ท้าทาย หากผู้ใช้ขาดการเชื่อมต่ออินเทอร์เน็ตหรือปิดเว็บไซต์หรือเว็บแอปพลิเคชัน ผู้ใช้จะสูญเสียไฟล์โมเดลที่ดาวน์โหลดมาบางส่วนและต้องเริ่มใหม่เมื่อกลับมาที่หน้าเว็บ การใช้ Background Fetch API เป็นการเพิ่มประสิทธิภาพแบบเป็นขั้นเป็นตอนจะช่วยปรับปรุงประสบการณ์ของผู้ใช้ได้อย่างมาก

Browser Support

  • Chrome: 74.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

ลงทะเบียน Service Worker

Background Fetch API กำหนดให้แอปของคุณลงทะเบียน Service Worker

if ('serviceWorker' in navigator) {   window.addEventListener('load', async () => {     const registration = await navigator.serviceWorker.register('sw.js');     console.log('Service worker registered for scope', registration.scope);   }); } 

ทริกเกอร์การดึงข้อมูลในเบื้องหลัง

ขณะที่เบราว์เซอร์ดึงข้อมูล จะมีการแสดงความคืบหน้าให้ผู้ใช้เห็นและบอกวิธียกเลิกการดาวน์โหลด เมื่อดาวน์โหลดเสร็จแล้วเบราว์เซอร์จะเริ่ม Service Worker และแอปพลิเคชันจะดำเนินการกับคำตอบได้

Background Fetch API ยังเตรียมการเรียกข้อมูลให้เริ่มขณะออฟไลน์ได้ด้วย การดาวน์โหลดจะเริ่มขึ้นทันทีที่ผู้ใช้เชื่อมต่ออีกครั้ง หากผู้ใช้ออฟไลน์อยู่ กระบวนการจะหยุดชั่วคราวจนกว่าผู้ใช้จะออนไลน์อีกครั้ง

ในตัวอย่างต่อไปนี้ ผู้ใช้คลิกปุ่มเพื่อดาวน์โหลด Gemma 2B ก่อนดึงข้อมูล เราจะตรวจสอบว่ามีการดาวน์โหลดและแคชโมเดลไว้ก่อนหน้านี้หรือไม่ เพื่อไม่ให้ใช้ทรัพยากรที่ไม่จำเป็น หากไม่ได้แคชไว้ เราจะเริ่มการดึงข้อมูลในเบื้องหลัง

const FETCH_ID = 'gemma-2b'; const MODEL_URL =   'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin';  downloadButton.addEventListener('click', async (event) => {   // If the model is already downloaded, return it from the cache.   const modelAlreadyDownloaded = await caches.match(MODEL_URL);   if (modelAlreadyDownloaded) {     const modelBlob = await modelAlreadyDownloaded.blob();     // Do something with the model.     console.log(modelBlob);     return;   }    // The model still needs to be downloaded.   // Feature detection and fallback to classic `fetch()`.   if (!('BackgroundFetchManager' in self)) {     try {       const response = await fetch(MODEL_URL);       if (!response.ok || response.status !== 200) {         throw new Error(`Download failed ${MODEL_URL}`);       }       const modelBlob = await response.blob();       // Do something with the model.       console.log(modelBlob);       return;     } catch (err) {       console.error(err);     }   }    // The service worker registration.   const registration = await navigator.serviceWorker.ready;    // Check if there's already a background fetch running for the `FETCH_ID`.   let bgFetch = await registration.backgroundFetch.get(FETCH_ID);    // If not, start a background fetch.   if (!bgFetch) {     bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, {       title: 'Gemma 2B model',       icons: [         {           src: 'icon.png',           size: '128x128',           type: 'image/png',         },       ],       downloadTotal: await getResourceSize(MODEL_URL),     });   } }); 

ฟังก์ชัน getResourceSize() จะแสดงผลขนาดการดาวน์โหลดเป็นไบต์ คุณติดตั้งใช้งานได้โดยส่งคำขอ HEAD

const getResourceSize = async (url) => {   try {     const response = await fetch(url, { method: 'HEAD' });     if (response.ok) {       return response.headers.get('Content-Length');     }     console.error(`HTTP error: ${response.status}`);     return 0;   } catch (error) {     console.error('Error fetching content size:', error);     return 0;   } }; 

ความคืบหน้าในการดาวน์โหลดรายงาน

เมื่อการดึงข้อมูลในเบื้องหลังเริ่มขึ้นแล้วเบราว์เซอร์จะแสดงBackgroundFetchRegistration คุณสามารถใช้เหตุการณ์นี้เพื่อแจ้งความคืบหน้าในการดาวน์โหลดให้ผู้ใช้ทราบได้progress

bgFetch.addEventListener('progress', (e) => {   // There's no download progress yet.   if (!bgFetch.downloadTotal) {     return;   }   // Something went wrong.   if (bgFetch.failureReason) {     console.error(bgFetch.failureReason);   }   if (bgFetch.result === 'success') {     return;   }   // Update the user about progress.   console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`); }); 

แจ้งให้ผู้ใช้และไคลเอ็นต์ทราบว่าการดึงข้อมูลเสร็จสมบูรณ์แล้ว

เมื่อการดึงข้อมูลในเบื้องหลังสําเร็จ Service Worker ของแอปจะได้รับการดําเนินการ backgroundfetchsuccess

โค้ดต่อไปนี้รวมอยู่ใน Service Worker การเรียกใช้ updateUI() ในช่วงท้ายช่วยให้คุณอัปเดตอินเทอร์เฟซของเบราว์เซอร์เพื่อแจ้งให้ผู้ใช้ทราบถึงการดึงข้อมูลในเบื้องหลังที่สำเร็จ สุดท้าย แจ้งลูกค้าเกี่ยวกับการดาวน์โหลดที่เสร็จสมบูรณ์ เช่น ใช้ postMessage()

self.addEventListener('backgroundfetchsuccess', (event) => {   // Get the background fetch registration.   const bgFetch = event.registration;    event.waitUntil(     (async () => {       // Open a cache named 'downloads'.       const cache = await caches.open('downloads');       // Go over all records in the background fetch registration.       // (In the running example, there's just one record, but this way       // the code is future-proof.)       const records = await bgFetch.matchAll();       // Wait for the response(s) to be ready, then cache it/them.       const promises = records.map(async (record) => {         const response = await record.responseReady;         await cache.put(record.request, response);       });       await Promise.all(promises);        // Update the browser UI.       event.updateUI({ title: 'Model downloaded' });        // Inform the clients that the model was downloaded.       self.clients.matchAll().then((clientList) => {         for (const client of clientList) {           client.postMessage({             message: 'download-complete',             id: bgFetch.id,           });         }       });     })(),   ); }); 

รับข้อความจาก Service Worker

หากต้องการรับข้อความแจ้งการส่งสําเร็จเกี่ยวกับการดาวน์โหลดที่เสร็จสมบูรณ์ในไคลเอ็นต์ ให้รับฟังเหตุการณ์ message เมื่อได้รับข้อความจาก Service Worker แล้ว คุณจะทํางานกับโมเดล AI และจัดเก็บโมเดลนั้นด้วย Cache API ได้

navigator.serviceWorker.addEventListener('message', async (event) => {   const cache = await caches.open('downloads');   const keys = await cache.keys();   for (const key of keys) {     const modelBlob = await cache       .match(key)       .then((response) => response.blob());     // Do something with the model.     console.log(modelBlob);   } }); 

ยกเลิกการดึงข้อมูลเบื้องหลัง

หากต้องการให้ผู้ใช้ยกเลิกการดาวน์โหลดที่ดำเนินอยู่ ให้ใช้abort()ของ BackgroundFetchRegistration

const registration = await navigator.serviceWorker.ready; const bgFetch = await registration.backgroundFetch.get(FETCH_ID); if (!bgFetch) {   return; } await bgFetch.abort(); 

แคชโมเดล

แคชโมเดลที่ดาวน์โหลดเพื่อให้ผู้ใช้ดาวน์โหลดโมเดลเพียงครั้งเดียว แม้ว่า Background Fetch API จะปรับปรุงประสบการณ์การดาวน์โหลด แต่คุณควรใช้โมเดลที่เล็กที่สุดเท่าที่จะเป็นไปได้ใน AI ฝั่งไคลเอ็นต์เสมอ

API เหล่านี้จะช่วยสร้างประสบการณ์การใช้งาน AI ฝั่งไคลเอ็นต์ที่ดีขึ้นให้แก่ผู้ใช้

สาธิต

คุณดูการใช้งานแนวทางนี้ได้ทั้งหมดในเดโมและซอร์สโค้ด

แผงแอปพลิเคชันของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome เปิดขึ้นเพื่อดาวน์โหลดการดึงข้อมูลในเบื้องหลัง
Chrome DevTools ช่วยให้คุณดูตัวอย่างเหตุการณ์ที่เกี่ยวข้องกับการดึงข้อมูลในเบื้องหลังอย่างต่อเนื่องได้ เดโมแสดงการดาวน์โหลดที่อยู่ระหว่างดำเนินการซึ่งดาวน์โหลดไปแล้ว 17.54 เมกะไบต์จากทั้งหมด 1.26 กิกะไบต์ สัญญาณบอกสถานะการดาวน์โหลดของเบราว์เซอร์จะแสดงการดาวน์โหลดที่ดำเนินอยู่ด้วย

ขอขอบคุณ

คู่มือนี้ได้รับการตรวจสอบโดย François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas และ Alexandra Klepper