FileSystemAPIUtils.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /**
  2. * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: AGPL-3.0-or-later
  4. */
  5. import { basename } from 'node:path'
  6. import mime from 'mime'
  7. class FileSystemEntry {
  8. private _isFile: boolean
  9. private _fullPath: string
  10. constructor(isFile: boolean, fullPath: string) {
  11. this._isFile = isFile
  12. this._fullPath = fullPath
  13. }
  14. get isFile() {
  15. return !!this._isFile
  16. }
  17. get isDirectory() {
  18. return !this.isFile
  19. }
  20. get name() {
  21. return basename(this._fullPath)
  22. }
  23. }
  24. export class FileSystemFileEntry extends FileSystemEntry {
  25. private _contents: string
  26. private _lastModified: number
  27. constructor(fullPath: string, contents: string, lastModified = Date.now()) {
  28. super(true, fullPath)
  29. this._contents = contents
  30. this._lastModified = lastModified
  31. }
  32. file(success: (file: File) => void) {
  33. const lastModified = this._lastModified
  34. // Faking the mime by using the file extension
  35. const type = mime.getType(this.name) || ''
  36. success(new File([this._contents], this.name, { lastModified, type }))
  37. }
  38. }
  39. export class FileSystemDirectoryEntry extends FileSystemEntry {
  40. private _entries: FileSystemEntry[]
  41. constructor(fullPath: string, entries: FileSystemEntry[]) {
  42. super(false, fullPath)
  43. this._entries = entries || []
  44. }
  45. createReader() {
  46. let read = false
  47. return {
  48. readEntries: (success: (entries: FileSystemEntry[]) => void) => {
  49. if (read) {
  50. return success([])
  51. }
  52. read = true
  53. success(this._entries)
  54. },
  55. }
  56. }
  57. }
  58. /**
  59. * This mocks the File API's File class
  60. * It will allow us to test the Filesystem API as well as the
  61. * File API in the same test suite.
  62. */
  63. export class DataTransferItem {
  64. private _type: string
  65. private _entry: FileSystemEntry
  66. getAsEntry?: () => FileSystemEntry
  67. constructor(type = '', entry: FileSystemEntry, isFileSystemAPIAvailable = true) {
  68. this._type = type
  69. this._entry = entry
  70. // Only when the Files API is available we are
  71. // able to get the entry
  72. if (isFileSystemAPIAvailable) {
  73. this.getAsEntry = () => this._entry
  74. }
  75. }
  76. get kind() {
  77. return 'file'
  78. }
  79. get type() {
  80. return this._type
  81. }
  82. getAsFile(): File|null {
  83. if (this._entry.isFile && this._entry instanceof FileSystemFileEntry) {
  84. let file: File | null = null
  85. this._entry.file((f) => {
  86. file = f
  87. })
  88. return file
  89. }
  90. // The browser will return an empty File object if the entry is a directory
  91. return new File([], this._entry.name, { type: '' })
  92. }
  93. }
  94. export const fileSystemEntryToDataTransferItem = (entry: FileSystemEntry, isFileSystemAPIAvailable = true): DataTransferItem => {
  95. return new DataTransferItem(
  96. entry.isFile ? 'text/plain' : 'httpd/unix-directory',
  97. entry,
  98. isFileSystemAPIAvailable,
  99. )
  100. }