indexedDBImageCache.ts 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import { defineNuxtPlugin } from '#app'
  2. export default defineNuxtPlugin((nuxtApp) => {
  3. // Открываем или создаём IndexedDB
  4. const dbPromise = new Promise<IDBDatabase>((resolve, reject) => {
  5. const request = indexedDB.open('image-cache-db', 1)
  6. request.onupgradeneeded = () => {
  7. const db = request.result
  8. if (!db.objectStoreNames.contains('images')) {
  9. db.createObjectStore('images', { keyPath: 'url' })
  10. }
  11. }
  12. request.onsuccess = () => resolve(request.result)
  13. request.onerror = () => reject(request.error)
  14. })
  15. /**
  16. * Возвращает blob-URL только после полной загрузки и кэширования.
  17. * Если в кэше есть Blob, сразу возвращает blob-URL.
  18. * Если нет, загружает картинку, сохраняет в IndexedDB и возвращает blob-URL.
  19. * При ошибке возвращает оригинальный URL.
  20. */
  21. async function getCachedUrl(url: string): Promise<string> {
  22. if (!url) return ''
  23. try {
  24. const db = await dbPromise
  25. // Проверяем наличие в кэше
  26. const cached = await new Promise<{ url: string; blob: Blob } | undefined>((resolve, reject) => {
  27. const tx = db.transaction('images', 'readonly')
  28. const store = tx.objectStore('images')
  29. const req = store.get(url)
  30. req.onsuccess = () => resolve(req.result)
  31. req.onerror = () => reject(req.error)
  32. })
  33. if (cached) {
  34. return URL.createObjectURL(cached.blob)
  35. }
  36. // Загружаем из сети
  37. const response = await fetch(url)
  38. if (!response.ok) throw new Error(`HTTP error ${response.status}`)
  39. const blob = await response.blob()
  40. // Сохраняем в кэш
  41. try {
  42. const tx2 = db.transaction('images', 'readwrite')
  43. tx2.objectStore('images').put({ url, blob })
  44. } catch (e) {
  45. console.error('Ошибка при сохранении в кэш:', e)
  46. }
  47. return URL.createObjectURL(blob)
  48. } catch (e) {
  49. console.error('getCachedUrl error:', e)
  50. // Возвращаем оригинальный URL при любой ошибке
  51. return url
  52. }
  53. }
  54. // Делаем функцию доступной через useNuxtApp().$getCachedUrl
  55. nuxtApp.provide('getCachedUrl', getCachedUrl)
  56. })
  57. /**
  58. Пример использования в компонентах:
  59. <script setup lang="ts">
  60. import { onMounted, ref } from 'vue'
  61. const props = defineProps<{ image: string }>()
  62. const cachedSrc = ref('')
  63. const { $getCachedUrl } = useNuxtApp()
  64. onMounted(async () => {
  65. // функция вернёт только после загрузки
  66. cachedSrc.value = await $getCachedUrl(props.image)
  67. })
  68. </script>
  69. <template>
  70. <NuxtImg
  71. :src="cachedSrc"
  72. alt="Возникла проблема с загрузкой фото, попробуйте позже."
  73. class="h-[200px] object-cover object-center"
  74. :height="200"
  75. custom
  76. :quality="10"
  77. />
  78. </template>
  79. */