@holochain-open-dev/file-storage 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/context.d.ts +3 -0
- package/dist/context.js +3 -0
- package/dist/context.js.map +1 -0
- package/dist/definitions/upload-files.d.ts +1 -0
- package/dist/definitions/upload-files.js +9 -0
- package/dist/definitions/upload-files.js.map +1 -0
- package/dist/elements/upload-files.d.ts +19 -0
- package/dist/elements/upload-files.js +59 -0
- package/dist/elements/upload-files.js.map +1 -0
- package/dist/holochain-dropzone.d.ts +8 -0
- package/dist/holochain-dropzone.js +30 -0
- package/dist/holochain-dropzone.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/services/file-storage.service.d.ts +40 -0
- package/dist/services/file-storage.service.js +89 -0
- package/dist/services/file-storage.service.js.map +1 -0
- package/dist/sharedStyles.d.ts +1 -0
- package/dist/sharedStyles.js +16 -0
- package/dist/sharedStyles.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @holochain-open-dev/file-storage
|
|
2
|
+
|
|
3
|
+
Frontend module for the Holochain zome `hc_zome_file_storage`.
|
|
4
|
+
|
|
5
|
+
This package includes types, a service and a collection of Custom Elements to build Holochain applications that automatically connect and interact with the `hc_zome_file_storage` zome.
|
|
6
|
+
|
|
7
|
+
By using [Custom Elements](https://developers.google.com/web/fundamentals/web-components/customelements), this package exports frontend blocks reusable in any framework, that make it really easy for consuming web applications to include functionality to create and update profiles, or search for an agent in the DHT.
|
|
8
|
+
|
|
9
|
+
Read about how to include both the zome and this frontend module in your application here:
|
|
10
|
+
|
|
11
|
+
- https://holochain-open-dev.github.io/file-storage
|
package/dist/context.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAW,MAAM,6BAA6B,CAAC;AAGrE,MAAM,CAAC,MAAM,yBAAyB,GACpC,aAAa,CAAC,0CAA0C,CAAC,CAAC","sourcesContent":["import { createContext, Context } from '@holochain-open-dev/context';\nimport { FileStorageService } from './services/file-storage.service';\n\nexport const fileStorageServiceContext: Context<FileStorageService> =\n createContext('hc_zome_file_storage/file-storage-servce');\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { UploadFiles } from '../elements/upload-files';
|
|
3
|
+
import { customElement } from 'lit/decorators.js';
|
|
4
|
+
let UF = class UF extends UploadFiles {
|
|
5
|
+
};
|
|
6
|
+
UF = __decorate([
|
|
7
|
+
customElement('upload-files')
|
|
8
|
+
], UF);
|
|
9
|
+
//# sourceMappingURL=upload-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-files.js","sourceRoot":"","sources":["../../src/definitions/upload-files.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,IAAM,EAAE,GAAR,MAAM,EAAG,SAAQ,WAAW;CAAG,CAAA;AAAzB,EAAE;IADP,aAAa,CAAC,cAAc,CAAC;GACxB,EAAE,CAAuB","sourcesContent":["import { UploadFiles } from '../elements/upload-files';\nimport { customElement } from 'lit/decorators.js';\n\n@customElement('upload-files')\nclass UF extends UploadFiles {}"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { FileStorageService } from '../services/file-storage.service';
|
|
3
|
+
declare const UploadFiles_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types/src/types").ScopedElementsHost>;
|
|
4
|
+
/**
|
|
5
|
+
* @fires file-uploaded - Fired after having uploaded the file
|
|
6
|
+
* @csspart dropzone - Style the dropzone itself
|
|
7
|
+
*/
|
|
8
|
+
export declare class UploadFiles extends UploadFiles_base {
|
|
9
|
+
/** Public attributes */
|
|
10
|
+
oneFile: boolean;
|
|
11
|
+
acceptedFiles: string | undefined;
|
|
12
|
+
/** Dependencies */
|
|
13
|
+
_service: FileStorageService;
|
|
14
|
+
/** Private properties */
|
|
15
|
+
firstUpdated(): void;
|
|
16
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
17
|
+
static get styles(): import("lit").CSSResult[];
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { css, html, LitElement } from 'lit';
|
|
3
|
+
import { property } from 'lit/decorators.js';
|
|
4
|
+
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
|
|
5
|
+
import { DropzoneElement } from '@scoped-elements/dropzone';
|
|
6
|
+
import { contextProvided } from '@holochain-open-dev/context';
|
|
7
|
+
import { sharedStyles } from '../sharedStyles';
|
|
8
|
+
import { HolochainDropzone } from '../holochain-dropzone';
|
|
9
|
+
import { fileStorageServiceContext } from '../context';
|
|
10
|
+
/**
|
|
11
|
+
* @fires file-uploaded - Fired after having uploaded the file
|
|
12
|
+
* @csspart dropzone - Style the dropzone itself
|
|
13
|
+
*/
|
|
14
|
+
export class UploadFiles extends ScopedElementsMixin(LitElement) {
|
|
15
|
+
constructor() {
|
|
16
|
+
/** Public attributes */
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.oneFile = false;
|
|
19
|
+
this.acceptedFiles = undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Private properties */
|
|
22
|
+
firstUpdated() {
|
|
23
|
+
const service = this._service;
|
|
24
|
+
this.defineScopedElement('drop-zone', class extends DropzoneElement {
|
|
25
|
+
buildDropzone(dropzoneElement, options) {
|
|
26
|
+
return new HolochainDropzone(dropzoneElement, service, options);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
return html `
|
|
32
|
+
<drop-zone
|
|
33
|
+
.oneFile=${this.oneFile}
|
|
34
|
+
.acceptedFiles=${this.acceptedFiles}
|
|
35
|
+
@file-uploaded=${(e) => (e.detail.hash = e.detail.file.hash)}
|
|
36
|
+
></drop-zone>
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
static get styles() {
|
|
40
|
+
return [
|
|
41
|
+
sharedStyles,
|
|
42
|
+
css `
|
|
43
|
+
:host {
|
|
44
|
+
display: contents;
|
|
45
|
+
}
|
|
46
|
+
`,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
__decorate([
|
|
51
|
+
property({ type: Boolean, attribute: 'one-file' })
|
|
52
|
+
], UploadFiles.prototype, "oneFile", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
property({ type: String, attribute: 'accepted-files' })
|
|
55
|
+
], UploadFiles.prototype, "acceptedFiles", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
contextProvided({ context: fileStorageServiceContext })
|
|
58
|
+
], UploadFiles.prototype, "_service", void 0);
|
|
59
|
+
//# sourceMappingURL=upload-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-files.js","sourceRoot":"","sources":["../../src/elements/upload-files.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAI9D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;GAGG;AACH,MAAM,OAAO,WAAY,SAAQ,mBAAmB,CAAC,UAAU,CAAC;IAAhE;QACE,wBAAwB;;QAE4B,YAAO,GAAG,KAAK,CAAC;QACX,kBAAa,GAEtD,SAAS,CAAC;IA4C5B,CAAC;IArCC,yBAAyB;IAEzB,YAAY;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE9B,IAAI,CAAC,mBAAmB,CACtB,WAAW,EACX,KAAM,SAAQ,eAAe;YAC3B,aAAa,CAAC,eAA4B,EAAE,OAAwB;gBAClE,OAAO,IAAI,iBAAiB,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAClE,CAAC;SACF,CACF,CAAC;IAEJ,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;;mBAEI,IAAI,CAAC,OAAO;yBACN,IAAI,CAAC,aAAa;yBAClB,CAAC,CAAc,EAAE,EAAE,CAClC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;;KAEzC,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,MAAM;QACf,OAAO;YACL,YAAY;YACZ,GAAG,CAAA;;;;OAIF;SACF,CAAC;IACJ,CAAC;CACF;AA/CqD;IAAnD,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;4CAAiB;AACX;IAAxD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;kDAE9B;AAK1B;IADC,eAAe,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;6CAC1B","sourcesContent":["import { css, html, LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements';\nimport { DropzoneElement } from '@scoped-elements/dropzone';\nimport { contextProvided } from '@holochain-open-dev/context';\nimport { DropzoneOptions } from 'dropzone';\n\nimport { FileStorageService } from '../services/file-storage.service';\nimport { sharedStyles } from '../sharedStyles';\nimport { HolochainDropzone } from '../holochain-dropzone';\nimport { fileStorageServiceContext } from '../context';\n\n/**\n * @fires file-uploaded - Fired after having uploaded the file\n * @csspart dropzone - Style the dropzone itself\n */\nexport class UploadFiles extends ScopedElementsMixin(LitElement) {\n /** Public attributes */\n\n @property({ type: Boolean, attribute: 'one-file' }) oneFile = false;\n @property({ type: String, attribute: 'accepted-files' }) acceptedFiles:\n | string\n | undefined = undefined;\n\n /** Dependencies */\n\n @contextProvided({ context: fileStorageServiceContext })\n _service!: FileStorageService;\n\n /** Private properties */\n\n firstUpdated() {\n const service = this._service;\n\n this.defineScopedElement(\n 'drop-zone',\n class extends DropzoneElement {\n buildDropzone(dropzoneElement: HTMLElement, options: DropzoneOptions) {\n return new HolochainDropzone(dropzoneElement, service, options);\n }\n }\n );\n\n }\n\n render() {\n return html`\n <drop-zone\n .oneFile=${this.oneFile}\n .acceptedFiles=${this.acceptedFiles}\n @file-uploaded=${(e: CustomEvent) =>\n (e.detail.hash = e.detail.file.hash)}\n ></drop-zone>\n `;\n }\n\n static get styles() {\n return [\n sharedStyles,\n css`\n :host {\n display: contents;\n }\n `,\n ];\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Dropzone, { DropzoneOptions } from 'dropzone';
|
|
2
|
+
import { FileStorageService } from './services/file-storage.service';
|
|
3
|
+
export declare class HolochainDropzone extends Dropzone {
|
|
4
|
+
fileStorageService: FileStorageService;
|
|
5
|
+
constructor(el: HTMLElement, fileStorageService: FileStorageService, options: DropzoneOptions);
|
|
6
|
+
uploadFiles(files: Dropzone.DropzoneFile[]): void;
|
|
7
|
+
_uploadFilesToHolochain(dropzoneFiles: Dropzone.DropzoneFile[]): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Dropzone from 'dropzone';
|
|
2
|
+
export class HolochainDropzone extends Dropzone {
|
|
3
|
+
constructor(el, fileStorageService, options) {
|
|
4
|
+
options.url = 'https://holochain.org/'; // just to bypass the check.
|
|
5
|
+
super(el, options);
|
|
6
|
+
this.fileStorageService = fileStorageService;
|
|
7
|
+
}
|
|
8
|
+
uploadFiles(files) {
|
|
9
|
+
this._uploadFilesToHolochain(files);
|
|
10
|
+
}
|
|
11
|
+
async _uploadFilesToHolochain(dropzoneFiles) {
|
|
12
|
+
for (const file of dropzoneFiles) {
|
|
13
|
+
try {
|
|
14
|
+
this.emit('sending', file, undefined, undefined);
|
|
15
|
+
const hash = await this.fileStorageService.uploadFile(file, (percentatge, bytesSent) => {
|
|
16
|
+
this.emit('uploadprogress', file, percentatge * 100, bytesSent);
|
|
17
|
+
});
|
|
18
|
+
this.emit('success', file, undefined);
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
file.hash = hash;
|
|
21
|
+
this.emit('complete', file);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.error(e);
|
|
25
|
+
this.emit('error', file, e.data.data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=holochain-dropzone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holochain-dropzone.js","sourceRoot":"","sources":["../src/holochain-dropzone.ts"],"names":[],"mappings":"AAAA,OAAO,QAA6B,MAAM,UAAU,CAAC;AAGrD,MAAM,OAAO,iBAAkB,SAAQ,QAAQ;IAE7C,YACE,EAAe,EACf,kBAAsC,EACtC,OAAwB;QAExB,OAAO,CAAC,GAAG,GAAG,wBAAwB,CAAC,CAAC,4BAA4B;QACpE,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACnB,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;IAC/C,CAAC;IAED,WAAW,CAAC,KAA8B;QACxC,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,uBAAuB,CAC3B,aAAsC;QAEtC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE;YAChC,IAAI;gBACF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;gBACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CACnD,IAAI,EACJ,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE;oBACzB,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,GAAG,EAAE,SAAS,CAAC,CAAC;gBAClE,CAAC,CACF,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBACtC,aAAa;gBACb,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;aAC7B;YAAC,OAAO,CAAC,EAAE;gBACV,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACjB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAG,CAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAChD;SACF;IACH,CAAC;CACF","sourcesContent":["import Dropzone, { DropzoneOptions } from 'dropzone';\nimport { FileStorageService } from './services/file-storage.service';\n\nexport class HolochainDropzone extends Dropzone {\n fileStorageService: FileStorageService;\n constructor(\n el: HTMLElement,\n fileStorageService: FileStorageService,\n options: DropzoneOptions\n ) {\n options.url = 'https://holochain.org/'; // just to bypass the check.\n super(el, options);\n this.fileStorageService = fileStorageService;\n }\n\n uploadFiles(files: Dropzone.DropzoneFile[]) {\n this._uploadFilesToHolochain(files);\n }\n\n async _uploadFilesToHolochain(\n dropzoneFiles: Dropzone.DropzoneFile[]\n ): Promise<void> {\n for (const file of dropzoneFiles) {\n try {\n this.emit('sending', file, undefined, undefined);\n const hash = await this.fileStorageService.uploadFile(\n file,\n (percentatge, bytesSent) => {\n this.emit('uploadprogress', file, percentatge * 100, bytesSent);\n }\n );\n this.emit('success', file, undefined);\n // @ts-ignore\n file.hash = hash;\n this.emit('complete', file);\n } catch (e) {\n console.error(e);\n this.emit('error', file, (e as any).data.data);\n }\n }\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC","sourcesContent":["export * from './elements/upload-files';\nexport * from './services/file-storage.service';\nexport * from './holochain-dropzone';\nexport * from './types';\nexport * from './context';\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CellClient } from '@holochain-open-dev/cell-client';
|
|
2
|
+
import { FileMetadata } from '../types';
|
|
3
|
+
export declare class FileStorageService {
|
|
4
|
+
protected cellClient: CellClient;
|
|
5
|
+
protected zomeName: string;
|
|
6
|
+
/**
|
|
7
|
+
* @param appWebsocket connection to the holochain backend
|
|
8
|
+
* @param cellId the cell to which to upload the file
|
|
9
|
+
* @param zomeName the zome name of the file_storage zome in the given cell
|
|
10
|
+
*/
|
|
11
|
+
constructor(cellClient: CellClient, zomeName?: string);
|
|
12
|
+
/**
|
|
13
|
+
* Upload a file to the file_storage zome, splitting it into chunks
|
|
14
|
+
*
|
|
15
|
+
* @param file file to split and upload
|
|
16
|
+
* @param chunkSize chunk size to split the file, default 256 KB
|
|
17
|
+
*/
|
|
18
|
+
uploadFile(file: File, onProgress?: undefined | ((percentatgeProgress: number, bytesSent: number) => void), chunkSize?: number): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Downloads the whole file with the given hash
|
|
21
|
+
* @param fileHash
|
|
22
|
+
*/
|
|
23
|
+
downloadFile(fileHash: string): Promise<File>;
|
|
24
|
+
/**
|
|
25
|
+
* Gets only the metadata of the file with the given hash
|
|
26
|
+
* This is specially useful if you want to fetch the chunks one by one
|
|
27
|
+
* @param fileHash the hash of the file
|
|
28
|
+
*/
|
|
29
|
+
getFileMetadata(fileHash: string): Promise<FileMetadata>;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch the chunk identified with the given hash
|
|
32
|
+
* This is useful if used with the chunk hashes received with `getFileMetadata`
|
|
33
|
+
* @param fileChunkHash
|
|
34
|
+
*/
|
|
35
|
+
fetchChunk(fileChunkHash: string): Promise<Blob>;
|
|
36
|
+
/** Private helpers */
|
|
37
|
+
private _splitFile;
|
|
38
|
+
private _createChunk;
|
|
39
|
+
private _callZome;
|
|
40
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export class FileStorageService {
|
|
2
|
+
/**
|
|
3
|
+
* @param appWebsocket connection to the holochain backend
|
|
4
|
+
* @param cellId the cell to which to upload the file
|
|
5
|
+
* @param zomeName the zome name of the file_storage zome in the given cell
|
|
6
|
+
*/
|
|
7
|
+
constructor(cellClient, zomeName = 'file_storage') {
|
|
8
|
+
this.cellClient = cellClient;
|
|
9
|
+
this.zomeName = zomeName;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Upload a file to the file_storage zome, splitting it into chunks
|
|
13
|
+
*
|
|
14
|
+
* @param file file to split and upload
|
|
15
|
+
* @param chunkSize chunk size to split the file, default 256 KB
|
|
16
|
+
*/
|
|
17
|
+
async uploadFile(file, onProgress = undefined, chunkSize = 256 * 1024) {
|
|
18
|
+
const blobs = this._splitFile(file, chunkSize);
|
|
19
|
+
const numberOfChunks = blobs.length;
|
|
20
|
+
const bytesPerChunk = blobs[0].size;
|
|
21
|
+
const chunksHashes = [];
|
|
22
|
+
for (let i = 0; i < blobs.length; i++) {
|
|
23
|
+
const chunkHash = await this._createChunk(blobs[i]);
|
|
24
|
+
chunksHashes.push(chunkHash);
|
|
25
|
+
if (onProgress) {
|
|
26
|
+
onProgress(((i + 1) * 1.0) / numberOfChunks, bytesPerChunk * (i + 1));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const fileToCreate = {
|
|
30
|
+
name: file.name,
|
|
31
|
+
size: file.size,
|
|
32
|
+
fileType: file.type,
|
|
33
|
+
lastModified: file.lastModified,
|
|
34
|
+
chunksHashes,
|
|
35
|
+
};
|
|
36
|
+
const hash = await this._callZome('create_file_metadata', fileToCreate);
|
|
37
|
+
return hash;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Downloads the whole file with the given hash
|
|
41
|
+
* @param fileHash
|
|
42
|
+
*/
|
|
43
|
+
async downloadFile(fileHash) {
|
|
44
|
+
const metadata = await this.getFileMetadata(fileHash);
|
|
45
|
+
const fetchChunksPromises = metadata.chunksHashes.map(hash => this.fetchChunk(hash));
|
|
46
|
+
const chunks = await Promise.all(fetchChunksPromises);
|
|
47
|
+
const file = new File(chunks, metadata.name, {
|
|
48
|
+
lastModified: metadata.lastModifed,
|
|
49
|
+
type: metadata.fileType,
|
|
50
|
+
});
|
|
51
|
+
return file;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets only the metadata of the file with the given hash
|
|
55
|
+
* This is specially useful if you want to fetch the chunks one by one
|
|
56
|
+
* @param fileHash the hash of the file
|
|
57
|
+
*/
|
|
58
|
+
async getFileMetadata(fileHash) {
|
|
59
|
+
return await this._callZome('get_file_metadata', fileHash);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Fetch the chunk identified with the given hash
|
|
63
|
+
* This is useful if used with the chunk hashes received with `getFileMetadata`
|
|
64
|
+
* @param fileChunkHash
|
|
65
|
+
*/
|
|
66
|
+
async fetchChunk(fileChunkHash) {
|
|
67
|
+
const bytes = await this._callZome('get_file_chunk', fileChunkHash);
|
|
68
|
+
return new Blob([new Uint8Array(bytes)]);
|
|
69
|
+
}
|
|
70
|
+
/** Private helpers */
|
|
71
|
+
_splitFile(file, chunkSize) {
|
|
72
|
+
let offset = 0;
|
|
73
|
+
const chunks = [];
|
|
74
|
+
while (file.size > offset) {
|
|
75
|
+
const chunk = file.slice(offset, offset + chunkSize);
|
|
76
|
+
offset += chunkSize;
|
|
77
|
+
chunks.push(chunk);
|
|
78
|
+
}
|
|
79
|
+
return chunks;
|
|
80
|
+
}
|
|
81
|
+
async _createChunk(chunk) {
|
|
82
|
+
const bytes = await chunk.arrayBuffer();
|
|
83
|
+
return this._callZome('create_file_chunk', new Uint8Array(bytes));
|
|
84
|
+
}
|
|
85
|
+
_callZome(fnName, payload) {
|
|
86
|
+
return this.cellClient.callZome(this.zomeName, fnName, payload);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=file-storage.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-storage.service.js","sourceRoot":"","sources":["../../src/services/file-storage.service.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CellClient } from '@holochain-open-dev/cell-client';\nimport { FileMetadata } from '../types';\n\nexport class FileStorageService {\n /**\n * @param appWebsocket connection to the holochain backend\n * @param cellId the cell to which to upload the file\n * @param zomeName the zome name of the file_storage zome in the given cell\n */\n constructor(\n protected cellClient: CellClient,\n protected zomeName: string = 'file_storage'\n ) {}\n\n /**\n * Upload a file to the file_storage zome, splitting it into chunks\n *\n * @param file file to split and upload\n * @param chunkSize chunk size to split the file, default 256 KB\n */\n async uploadFile(\n file: File,\n onProgress:\n | undefined\n | ((percentatgeProgress: number, bytesSent: number) => void) = undefined,\n chunkSize: number = 256 * 1024\n ): Promise<string> {\n const blobs = this._splitFile(file, chunkSize);\n const numberOfChunks = blobs.length;\n const bytesPerChunk = blobs[0].size;\n\n const chunksHashes: Array<string> = [];\n for (let i = 0; i < blobs.length; i++) {\n const chunkHash = await this._createChunk(blobs[i]);\n chunksHashes.push(chunkHash);\n if (onProgress) {\n onProgress(((i + 1) * 1.0) / numberOfChunks, bytesPerChunk * (i + 1));\n }\n }\n\n const fileToCreate = {\n name: file.name,\n size: file.size,\n fileType: file.type,\n lastModified: file.lastModified,\n chunksHashes,\n };\n const hash = await this._callZome('create_file_metadata', fileToCreate);\n\n return hash;\n }\n\n /**\n * Downloads the whole file with the given hash\n * @param fileHash\n */\n async downloadFile(fileHash: string): Promise<File> {\n const metadata = await this.getFileMetadata(fileHash);\n\n const fetchChunksPromises = metadata.chunksHashes.map(hash =>\n this.fetchChunk(hash)\n );\n\n const chunks = await Promise.all(fetchChunksPromises);\n\n const file = new File(chunks, metadata.name, {\n lastModified: metadata.lastModifed,\n type: metadata.fileType,\n });\n\n return file;\n }\n\n /**\n * Gets only the metadata of the file with the given hash\n * This is specially useful if you want to fetch the chunks one by one\n * @param fileHash the hash of the file\n */\n async getFileMetadata(fileHash: string): Promise<FileMetadata> {\n return await this._callZome('get_file_metadata', fileHash);\n }\n\n /**\n * Fetch the chunk identified with the given hash\n * This is useful if used with the chunk hashes received with `getFileMetadata`\n * @param fileChunkHash\n */\n async fetchChunk(fileChunkHash: string): Promise<Blob> {\n const bytes = await this._callZome('get_file_chunk', fileChunkHash);\n\n return new Blob([new Uint8Array(bytes)]);\n }\n\n /** Private helpers */\n\n private _splitFile(file: File, chunkSize: number): Blob[] {\n let offset = 0;\n const chunks: Blob[] = [];\n\n while (file.size > offset) {\n const chunk = file.slice(offset, offset + chunkSize);\n offset += chunkSize;\n chunks.push(chunk);\n }\n\n return chunks;\n }\n\n private async _createChunk(chunk: Blob): Promise<string> {\n const bytes = await chunk.arrayBuffer();\n\n return this._callZome('create_file_chunk', new Uint8Array(bytes));\n }\n\n private _callZome(fnName: string, payload: any): Promise<any> {\n return this.cellClient.callZome(this.zomeName, fnName, payload);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sharedStyles: import("lit").CSSResult;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
export const sharedStyles = css `
|
|
3
|
+
.column {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
}
|
|
7
|
+
.row {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
}
|
|
11
|
+
.center-content {
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
//# sourceMappingURL=sharedStyles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharedStyles.js","sourceRoot":"","sources":["../src/sharedStyles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;;;;;;;;;;;;;CAa9B,CAAC","sourcesContent":["import { css } from 'lit';\n\nexport const sharedStyles = css`\n .column {\n display: flex;\n flex-direction: column;\n }\n .row {\n display: flex;\n flex-direction: row;\n }\n .center-content {\n align-items: center;\n justify-content: center;\n }\n`;\n"]}
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface FileMetadata {\n name: string;\n lastModifed: number;\n size: number;\n fileType: string;\n creatorPubKey: string;\n chunksHashes: Array<string>;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@holochain-open-dev/file-storage",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "File storage utilities to store files in holochain DHT",
|
|
5
|
+
"author": "guillem.cordoba@gmail.com",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js",
|
|
11
|
+
"./upload-files": "./dist/definitions/upload-files.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "npm run build && concurrently -k --names tsc,dev-server \"npm run build-watch\" \"web-dev-server --config demo/web-dev-server.config.mjs\"",
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"build-watch": "tsc -w --preserveWatchOutput",
|
|
21
|
+
"test": "npm run build && web-test-runner --coverage --puppeteer",
|
|
22
|
+
"test-debug": "npm run build && DEBUG=true web-test-runner --coverage --puppeteer",
|
|
23
|
+
"test-watch": "web-test-runner --watch --puppeteer",
|
|
24
|
+
"e2e": "CONDUCTOR_URL=ws://localhost:8888 concurrently -k -s first \"npm:test\" \"npm:start-holochain\"",
|
|
25
|
+
"analyze": "wca analyze src --format json --outFile custom-elements.json",
|
|
26
|
+
"lint": "eslint --ext .ts,.html . --ignore-path .gitignore",
|
|
27
|
+
"format": "eslint --ext .ts,.html . --fix --ignore-path .gitignore"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@holochain/client": "^0.3.2",
|
|
31
|
+
"@holochain-open-dev/cell-client": "^0.3.2",
|
|
32
|
+
"@holochain-open-dev/context": "^0.0.3",
|
|
33
|
+
"@open-wc/scoped-elements": "^2.0.1",
|
|
34
|
+
"@scoped-elements/dropzone": "^0.0.3",
|
|
35
|
+
"@scoped-elements/material-web": "^0.0.19",
|
|
36
|
+
"lit": "^2.2.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@open-wc/eslint-config": "^2.1.0",
|
|
40
|
+
"@open-wc/testing": "^3.0.0-next.5",
|
|
41
|
+
"@open-wc/testing-karma": "^4.0.9",
|
|
42
|
+
"@rollup/plugin-commonjs": "^15.1.0",
|
|
43
|
+
"@rollup/plugin-node-resolve": "^9.0.0",
|
|
44
|
+
"@rollup/plugin-replace": "^2.4.2",
|
|
45
|
+
"@rollup/plugin-typescript": "^8.2.1",
|
|
46
|
+
"@types/dropzone": "^5.7.0",
|
|
47
|
+
"@types/node": "13.11.1",
|
|
48
|
+
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
|
49
|
+
"@typescript-eslint/parser": "^2.34.0",
|
|
50
|
+
"@web/dev-server": "0.0.13",
|
|
51
|
+
"@web/dev-server-rollup": "^0.2.9",
|
|
52
|
+
"@web/test-runner": "^0.7.41",
|
|
53
|
+
"@web/test-runner-puppeteer": "^0.6.4",
|
|
54
|
+
"buffer": "^5.6.0",
|
|
55
|
+
"concurrently": "^5.1.0",
|
|
56
|
+
"deepmerge": "^3.2.0",
|
|
57
|
+
"es-dev-server": "^1.23.0",
|
|
58
|
+
"eslint": "^6.8.0",
|
|
59
|
+
"eslint-config-prettier": "^6.15.0",
|
|
60
|
+
"gh-pages": "^3.1.0",
|
|
61
|
+
"husky": "^1.0.0",
|
|
62
|
+
"lint-staged": "^10.0.0",
|
|
63
|
+
"prettier": "^2.0.4",
|
|
64
|
+
"rimraf": "^3.0.2",
|
|
65
|
+
"rollup": "^2.32.0",
|
|
66
|
+
"rollup-plugin-node-builtins": "^2.1.2",
|
|
67
|
+
"rollup-plugin-node-globals": "^1.4.0",
|
|
68
|
+
"rollup-plugin-postcss": "^3.1.8",
|
|
69
|
+
"rollup-plugin-postcss-lit": "^1.0.1",
|
|
70
|
+
"tslib": "^1.11.0",
|
|
71
|
+
"typescript": "~4.2.4",
|
|
72
|
+
"web-component-analyzer": "^1.1.6"
|
|
73
|
+
},
|
|
74
|
+
"eslintConfig": {
|
|
75
|
+
"extends": [
|
|
76
|
+
"@open-wc/eslint-config",
|
|
77
|
+
"eslint-config-prettier"
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"prettier": {
|
|
81
|
+
"singleQuote": true,
|
|
82
|
+
"arrowParens": "avoid"
|
|
83
|
+
},
|
|
84
|
+
"publishConfig": {
|
|
85
|
+
"access": "public"
|
|
86
|
+
},
|
|
87
|
+
"customElements": "custom-elements.json",
|
|
88
|
+
"type": "module"
|
|
89
|
+
}
|