file-uploader.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /* @vite-ignore */
  2. import * as exifr from 'exifr';
  3. import { serverEndpoint } from '../constants';
  4. import { uploadAssetsStore } from '$lib/stores/upload';
  5. import type { UploadAsset } from '../models/upload-asset';
  6. import { api, AssetFileUploadResponseDto } from '@api';
  7. import { albumUploadAssetStore } from '$lib/stores/album-upload-asset';
  8. /**
  9. * Determine if the upload is for album or for the user general backup
  10. * @variant GENERAL - Upload assets to the server for general backup
  11. * @variant ALBUM - Upload assets to the server for backup and add to the album
  12. */
  13. export enum UploadType {
  14. /**
  15. * Upload assets to the server
  16. */
  17. GENERAL = 'GENERAL',
  18. /**
  19. * Upload assets to the server and add to album
  20. */
  21. ALBUM = 'ALBUM'
  22. }
  23. export const openFileUploadDialog = (uploadType: UploadType) => {
  24. try {
  25. let fileSelector = document.createElement('input');
  26. fileSelector.type = 'file';
  27. fileSelector.multiple = true;
  28. fileSelector.accept = 'image/*,video/*,.heic,.heif';
  29. fileSelector.onchange = async (e: any) => {
  30. const files = Array.from<File>(e.target.files);
  31. const acceptedFile = files.filter(
  32. (e) => e.type.split('/')[0] === 'video' || e.type.split('/')[0] === 'image'
  33. );
  34. if (uploadType === UploadType.ALBUM) {
  35. albumUploadAssetStore.asset.set([]);
  36. albumUploadAssetStore.count.set(acceptedFile.length);
  37. }
  38. for (const asset of acceptedFile) {
  39. await fileUploader(asset, uploadType);
  40. }
  41. };
  42. fileSelector.click();
  43. } catch (e) {
  44. console.log('Error seelcting file', e);
  45. }
  46. };
  47. async function fileUploader(asset: File, uploadType: UploadType) {
  48. const assetType = asset.type.split('/')[0].toUpperCase();
  49. const temp = asset.name.split('.');
  50. const fileExtension = temp[temp.length - 1];
  51. const formData = new FormData();
  52. try {
  53. let exifData = null;
  54. if (assetType !== 'VIDEO') {
  55. exifData = await exifr.parse(asset);
  56. }
  57. const createdAt =
  58. exifData && exifData.DateTimeOriginal != null
  59. ? new Date(exifData.DateTimeOriginal).toISOString()
  60. : new Date(asset.lastModified).toISOString();
  61. const deviceAssetId = 'web' + '-' + asset.name + '-' + asset.lastModified;
  62. // Create and add Unique ID of asset on the device
  63. formData.append('deviceAssetId', deviceAssetId);
  64. // Get device id - for web -> use WEB
  65. formData.append('deviceId', 'WEB');
  66. // Get asset type
  67. formData.append('assetType', assetType);
  68. // Get Asset Created Date
  69. formData.append('createdAt', createdAt);
  70. // Get Asset Modified At
  71. formData.append('modifiedAt', new Date(asset.lastModified).toISOString());
  72. // Set Asset is Favorite to false
  73. formData.append('isFavorite', 'false');
  74. // Get asset duration
  75. formData.append('duration', '0:00:00.000000');
  76. // Get asset file extension
  77. formData.append('fileExtension', '.' + fileExtension);
  78. // Get asset binary data.
  79. formData.append('assetData', asset);
  80. // Check if asset upload on server before performing upload
  81. const { data, status } = await api.assetApi.checkDuplicateAsset({
  82. deviceAssetId: String(deviceAssetId),
  83. deviceId: 'WEB'
  84. });
  85. if (status === 200) {
  86. if (data.isExist) {
  87. if (uploadType === UploadType.ALBUM && data.id) {
  88. albumUploadAssetStore.asset.update((a) => {
  89. return [...a, data.id!];
  90. });
  91. }
  92. return;
  93. }
  94. }
  95. const request = new XMLHttpRequest();
  96. request.upload.onloadstart = () => {
  97. const newUploadAsset: UploadAsset = {
  98. id: deviceAssetId,
  99. file: asset,
  100. progress: 0,
  101. fileExtension: fileExtension
  102. };
  103. uploadAssetsStore.addNewUploadAsset(newUploadAsset);
  104. };
  105. request.upload.onload = (e) => {
  106. setTimeout(() => {
  107. uploadAssetsStore.removeUploadAsset(deviceAssetId);
  108. }, 1000);
  109. };
  110. request.onreadystatechange = () => {
  111. try {
  112. if (request.readyState === 4 && uploadType === UploadType.ALBUM) {
  113. const res: AssetFileUploadResponseDto = JSON.parse(request.response);
  114. albumUploadAssetStore.asset.update((assets) => {
  115. return [...assets, res.id];
  116. });
  117. }
  118. } catch (e) {
  119. console.error('ERROR parsing data JSON in upload onreadystatechange');
  120. }
  121. };
  122. // listen for `error` event
  123. request.upload.onerror = () => {
  124. uploadAssetsStore.removeUploadAsset(deviceAssetId);
  125. };
  126. // listen for `abort` event
  127. request.upload.onabort = () => {
  128. uploadAssetsStore.removeUploadAsset(deviceAssetId);
  129. };
  130. // listen for `progress` event
  131. request.upload.onprogress = (event) => {
  132. const percentComplete = Math.floor((event.loaded / event.total) * 100);
  133. uploadAssetsStore.updateProgress(deviceAssetId, percentComplete);
  134. };
  135. request.open('POST', `api/asset/upload`);
  136. request.send(formData);
  137. } catch (e) {
  138. console.log('error uploading file ', e);
  139. }
  140. }