imageManager.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import type ApiRequestService from './apiRequestService'
  2. import FileUtils from '~/services/utils/fileUtils'
  3. import { FILE_TYPE, FILE_VISIBILITY, IMAGE_SIZE } from '~/types/enum/enums'
  4. import type { AssociativeArray } from '~/types/data'
  5. import UrlUtils from '~/services/utils/urlUtils'
  6. /**
  7. * Permet le requêtage, l'upload et la manipulation des images via l'API Opentalent
  8. */
  9. class ImageManager {
  10. private apiRequestService: ApiRequestService
  11. public static readonly defaultImage = '/images/default/picture.jpeg'
  12. public constructor(apiRequestService: ApiRequestService) {
  13. this.apiRequestService = apiRequestService
  14. }
  15. /**
  16. * Retourne l'image correspondante sous forme soit d'une URL, soit d'un blob encodé au format base64.
  17. *
  18. * Retourne l'url de l'image par défaut si l'image est introuvable
  19. * ou si l'id passé en paramètre est null.
  20. *
  21. * @param id The id of the image; if null, the url to the default image is returned
  22. * @param size
  23. * @param defaultImage The path of an image in the 'public' folder, default: '/images/default/picture.jpeg'
  24. */
  25. public get(
  26. id: number | string | null,
  27. size: IMAGE_SIZE = IMAGE_SIZE.MD,
  28. defaultImage: string | null = null,
  29. ): Promise<string> {
  30. const defaultUrl = defaultImage ?? ImageManager.defaultImage
  31. if (id === null) {
  32. return Promise.resolve(defaultUrl)
  33. }
  34. const matches = id.toString().match(/\/api\/files\/(\d+)(?:\/\w+)?/)
  35. if (matches) {
  36. // Lors de l'enregistrement d'une entité, les ids des objets liés sont
  37. // temporairement convertis en IRI. Avec la réactivité, ceci peut
  38. // générer une erreur temporaire avec les liens des images, d'où ce patch.
  39. id = parseInt(matches[1])
  40. }
  41. if (!(typeof id === 'number' && Number.isInteger(id))) {
  42. throw new TypeError('Error: image ' + id + ' is invalid')
  43. }
  44. try {
  45. return size === IMAGE_SIZE.RAW
  46. ? this.getRaw(id)
  47. : this.getProcessed(id, size)
  48. } catch (error) {
  49. console.error(error)
  50. return Promise.resolve(defaultUrl)
  51. }
  52. }
  53. /**
  54. * Retourne une image dimensionnée et cropped depuis le cache Liip.
  55. *
  56. * @param id The id of the image; if null, the url to the default image is returned
  57. * @param size
  58. */
  59. private async getProcessed(
  60. id: number | null,
  61. size: IMAGE_SIZE = IMAGE_SIZE.MD,
  62. ): Promise<string> {
  63. const imageUrl = `api/image/download/${id}/${size}`
  64. // Une image doit toujours avoir le time en options pour éviter les problèmes de cache
  65. const query: AssociativeArray = { 0: this.getCacheKey() }
  66. const response = await this.apiRequestService.get(imageUrl, query)
  67. const cachedImageUrl = response.toString()
  68. if (!cachedImageUrl) {
  69. throw new Error('Error: image ' + id + ' not found')
  70. }
  71. return UrlUtils.addQuery(cachedImageUrl, query)
  72. }
  73. /**
  74. * Retourne l'image non traitée. Utilisé entre autres pour le
  75. * cropper du UiInputImage.
  76. *
  77. * @param id
  78. * @private
  79. */
  80. private async getRaw(id: number | null): Promise<string> {
  81. const imageUrl = `api/file/download/${id}`
  82. // Une image doit toujours avoir le time en options pour éviter les problèmes de cache
  83. const query = [this.getCacheKey()]
  84. const blobPart = await this.apiRequestService.get(imageUrl, query)
  85. if (!blobPart) {
  86. throw new Error('Error: image ' + id + ' not found')
  87. }
  88. if (!(blobPart instanceof Blob) || blobPart.size === 0) {
  89. throw new Error('Error: image ' + id + ' is invalid')
  90. }
  91. return await this.toBase64(blobPart)
  92. }
  93. public async upload(
  94. filename: string,
  95. content: string,
  96. visibility: string = FILE_VISIBILITY.NOBODY,
  97. config: string | null = null,
  98. ) {
  99. content = content.replace(/^data:image\/[\w/]+;base64,/, '')
  100. const data = JSON.stringify({
  101. filename,
  102. content,
  103. type: FILE_TYPE.UPLOADED,
  104. visibility,
  105. config,
  106. })
  107. return await this.apiRequestService.post('api/upload', data)
  108. }
  109. /**
  110. * Convert the API response into base64
  111. * @param data
  112. * @protected
  113. */
  114. protected async toBase64(data: BlobPart) {
  115. const blob = FileUtils.newBlob(data)
  116. return (await FileUtils.blobToBase64(blob)) ?? ''
  117. }
  118. /**
  119. * On passe cette clé en paramètres des requêtes pour éviter les problèmes de cache
  120. *
  121. * @protected
  122. */
  123. protected getCacheKey() {
  124. return new Date().getTime().toString()
  125. }
  126. }
  127. export default ImageManager