diff --git a/README.md b/README.md
index 6d95dc7..f58173d 100644
--- a/README.md
+++ b/README.md
@@ -248,6 +248,10 @@ further processing after downloading.
+## Utils
+
+See [docs/utils/README.md](/docs/utils/README.md).
+
## Changelog
See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-cloudinary/blob/main/CHANGELOG.md).
diff --git a/docs/utils/README.md b/docs/utils/README.md
new file mode 100644
index 0000000..2aedbee
--- /dev/null
+++ b/docs/utils/README.md
@@ -0,0 +1,117 @@
+# Utils
+
+## API
+
+
+
+* [`uploadResourceAsBlob(...)`](#uploadresourceasblob)
+* [`downloadResourceAsBlob(...)`](#downloadresourceasblob)
+* [Interfaces](#interfaces)
+* [Type Aliases](#type-aliases)
+* [Enums](#enums)
+
+
+
+
+
+
+### uploadResourceAsBlob(...)
+
+```typescript
+uploadResourceAsBlob(options: UploadResourceAsBlobOptions) => Promise
+```
+
+Upload a file to Cloudinary as a blob.
+
+| Param | Type |
+| ------------- | ----------------------------------------------------------------------------------- |
+| **`options`** | UploadResourceAsBlobOptions
|
+
+**Returns:** Promise<UploadResourceResult>
+
+**Since:** 0.1.1
+
+--------------------
+
+
+### downloadResourceAsBlob(...)
+
+```typescript
+downloadResourceAsBlob(options: DownloadResourceAsBlobOptions) => Promise
+```
+
+Download a file from Cloudinary as a blob.
+
+| Param | Type |
+| ------------- | --------------------------------------------------------------------------- |
+| **`options`** | DownloadResourceOptions
|
+
+**Returns:** Promise<DownloadResourceAsBlobResult>
+
+**Since:** 0.1.1
+
+--------------------
+
+
+### Interfaces
+
+
+#### UploadResourceResult
+
+| Prop | Type | Description | Since |
+| ---------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----- |
+| **`assetId`** | string
| The unique asset identifier of the uploaded resource. Only available on Android and Web. | 0.0.1 |
+| **`bytes`** | number
| The number of bytes of the uploaded resource. Only available on Android and Web. | 0.0.1 |
+| **`createdAt`** | string
| The timestamp at which the resource was uploaded. | 0.0.1 |
+| **`format`** | string
| The format of the uploaded resource. | 0.0.1 |
+| **`originalFilename`** | string
| The original filename of the uploaded resource. Only available on Android and iOS. | 0.0.1 |
+| **`resourceType`** | ResourceType
| The resource type of the uploaded resource. | 0.0.1 |
+| **`publicId`** | string
| The unique public identifier of the uploaded resource. | 0.0.1 |
+| **`url`** | string
| The url of the uploaded resource. | 0.0.1 |
+
+
+#### UploadResourceAsBlobOptions
+
+| Prop | Type | Description | Since |
+| ------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------- | ----- |
+| **`cloudName`** | string
| The cloud name of your app which you can find in the Cloudinary Management Console. | 0.1.1 |
+| **`resourceType`** | ResourceType
| The resource type to upload. | 0.1.1 |
+| **`blob`** | Blob
| The file to upload. | 0.1.1 |
+| **`uploadPreset`** | string
| The selected upload preset. | 0.1.1 |
+| **`publicId`** | string
| Assign a unique public identifier to the resource. | 0.1.1 |
+
+
+#### DownloadResourceAsBlobResult
+
+| Prop | Type | Description | Since |
+| ---------- | ----------------- | ---------------------------------- | ----- |
+| **`blob`** | Blob
| The downloaded resource as a blob. | 0.1.1 |
+
+
+#### DownloadResourceOptions
+
+| Prop | Type | Description | Since |
+| --------- | ------------------- | ------------------------------------ | ----- |
+| **`url`** | string
| The url of the resource to download. | 0.0.3 |
+
+
+### Type Aliases
+
+
+#### DownloadResourceAsBlobOptions
+
+DownloadResourceOptions
+
+
+### Enums
+
+
+#### ResourceType
+
+| Members | Value | Since |
+| ----------- | -------------------- | ----- |
+| **`Image`** | 'image'
| 0.0.1 |
+| **`Video`** | 'video'
| 0.0.1 |
+| **`Raw`** | 'raw'
| 0.0.1 |
+
+
diff --git a/package.json b/package.json
index 13cb225..e2f0ce2 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,9 @@
"eslint": "eslint . --ext ts",
"prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
"swiftlint": "node-swiftlint",
- "docgen": "docgen --api CloudinaryPlugin --output-readme README.md --output-json dist/docs.json",
+ "docgen": "npm run docgen:plugin && npm run docgen:utils",
+ "docgen:plugin": "docgen --api CloudinaryPlugin --output-readme README.md --output-json dist/docs.json",
+ "docgen:utils": "docgen --api ICloudinaryUtils --output-readme docs/utils/README.md --output-json dist/utils-docs.json",
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
"clean": "rimraf ./dist",
"watch": "tsc --watch",
diff --git a/src/definitions.ts b/src/definitions.ts
index 90573df..530032a 100644
--- a/src/definitions.ts
+++ b/src/definitions.ts
@@ -199,3 +199,85 @@ export enum ResourceType {
*/
Raw = 'raw',
}
+
+/**
+ * @since 0.1.1
+ */
+export interface ICloudinaryUtils {
+ /**
+ * Upload a file to Cloudinary as a blob.
+ *
+ * @since 0.1.1
+ */
+ uploadResourceAsBlob(
+ options: UploadResourceAsBlobOptions,
+ ): Promise;
+ /**
+ * Download a file from Cloudinary as a blob.
+ *
+ * @since 0.1.1
+ */
+ downloadResourceAsBlob(
+ options: DownloadResourceAsBlobOptions,
+ ): Promise;
+}
+
+/**
+ * @since 0.1.1
+ */
+export interface UploadResourceAsBlobOptions {
+ /**
+ * The cloud name of your app which you can find in the Cloudinary Management Console.
+ *
+ * @since 0.1.1
+ */
+ cloudName: string;
+ /**
+ * The resource type to upload.
+ *
+ * @since 0.1.1
+ */
+ resourceType: ResourceType;
+ /**
+ * The file to upload.
+ *
+ * @since 0.1.1
+ */
+ blob: Blob;
+ /**
+ * The selected upload preset.
+ *
+ * @since 0.1.1
+ * @see https://cloudinary.com/documentation/upload_presets
+ */
+ uploadPreset: string;
+ /**
+ * Assign a unique public identifier to the resource.
+ *
+ * @since 0.1.1
+ * @see https://cloudinary.com/documentation/upload_images#public_id
+ */
+ publicId?: string;
+}
+
+/**
+ * @since 0.1.1
+ */
+export type UploadResourceAsBlobResult = UploadResourceResult;
+
+/**
+ * @since 0.1.1
+ */
+export type DownloadResourceAsBlobOptions = DownloadResourceOptions;
+
+/**
+ * @since 0.1.1
+ */
+export interface DownloadResourceAsBlobResult {
+ /**
+ * The downloaded resource as a blob.
+ *
+ * @since 0.1.1
+ */
+ blob: Blob;
+}
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..4838cd6
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,104 @@
+import type {
+ ResourceType,
+ ICloudinaryUtils,
+ UploadResourceAsBlobOptions,
+ DownloadResourceAsBlobOptions,
+ DownloadResourceAsBlobResult,
+ UploadResourceAsBlobResult,
+} from './definitions';
+
+export class CloudinaryUtils implements ICloudinaryUtils {
+ public async uploadResourceAsBlob(
+ options: UploadResourceAsBlobOptions,
+ ): Promise {
+ const uniqueUploadId = this.generateUniqueId();
+ const chunkSize = 1024 * 1024 * 10; // 10 Megabytes
+ const totalSize = options.blob.size;
+ const chunks: { start: number; end: number; blob: Blob }[] = [];
+
+ let start = 0;
+ let end = Math.min(chunkSize, totalSize);
+ while (start < totalSize) {
+ const blob = options.blob.slice(start, end);
+ chunks.push({ start, end, blob });
+ start = end;
+ end = Math.min(start + chunkSize, totalSize);
+ }
+ let response: any;
+ for (const chunk of chunks) {
+ const { start, end, blob } = chunk;
+ response = await this.uploadResourceChunk(
+ options,
+ uniqueUploadId,
+ start,
+ end - 1,
+ totalSize,
+ blob,
+ );
+ }
+ return {
+ assetId: response.asset_id,
+ bytes: response.bytes,
+ createdAt: response.created_at,
+ format: response.format,
+ originalFilename: response.original_filename,
+ resourceType: response.resource_type,
+ publicId: response.public_id,
+ url: response.secure_url,
+ };
+ }
+
+ public async downloadResourceAsBlob(
+ options: DownloadResourceAsBlobOptions,
+ ): Promise {
+ const blob = await fetch(options.url).then(res => res.blob());
+ return {
+ blob,
+ };
+ }
+
+ private async uploadResourceChunk(
+ options: {
+ cloudName: string;
+ resourceType: ResourceType;
+ blob: Blob;
+ uploadPreset: string;
+ publicId?: string;
+ },
+ uniqueUploadId: string,
+ start: number,
+ end: number,
+ size: number,
+ chunk: Blob,
+ ): Promise {
+ const formData = new FormData();
+ formData.append('file', chunk);
+ formData.append('upload_preset', options.uploadPreset);
+ formData.append('cloud_name', options.cloudName);
+ if (options.publicId) {
+ formData.append('public_id', options.publicId);
+ }
+ return fetch(
+ `https://api.cloudinary.com/v1_1/${options.cloudName}/${options.resourceType}/upload`,
+ {
+ method: 'PUT',
+ body: formData,
+ headers: {
+ 'X-Unique-Upload-Id': uniqueUploadId,
+ 'Content-Range': `bytes ${start}-${end}/${size}`,
+ },
+ },
+ ).then(async response => {
+ if (!response.ok) {
+ throw new Error(
+ `Request failed with status ${response.status}: ${response.statusText}`,
+ );
+ }
+ return response.json();
+ });
+ }
+
+ private generateUniqueId(): string {
+ return Date.now().toString(36) + Math.random().toString(36).substring(2);
+ }
+}
diff --git a/src/web.ts b/src/web.ts
index 0a1bcf8..b09c489 100644
--- a/src/web.ts
+++ b/src/web.ts
@@ -8,10 +8,12 @@ import type {
UploadResourceOptions,
UploadResourceResult,
} from './definitions';
+import { CloudinaryUtils } from './utils';
export class CloudinaryWeb extends WebPlugin implements CloudinaryPlugin {
public static readonly ERROR_NOT_INITIALIZED = 'Plugin is not initialized.';
public static readonly ERROR_FILE_MISSING = 'blob must be provided.';
+ private readonly cloudinaryUtils = new CloudinaryUtils();
private cloudName?: string;
@@ -22,94 +24,22 @@ export class CloudinaryWeb extends WebPlugin implements CloudinaryPlugin {
public async uploadResource(
options: UploadResourceOptions,
): Promise {
+ if (!this.cloudName) {
+ throw new Error(CloudinaryWeb.ERROR_NOT_INITIALIZED);
+ }
if (!options.blob) {
throw new Error(CloudinaryWeb.ERROR_FILE_MISSING);
}
- const uniqueUploadId = this.generateUniqueId();
- const chunkSize = 1024 * 1024 * 10; // 10 Megabytes
- const totalSize = options.blob.size;
- const chunks: { start: number; end: number; blob: Blob }[] = [];
-
- let start = 0;
- let end = Math.min(chunkSize, totalSize);
- while (start < totalSize) {
- const blob = options.blob.slice(start, end);
- chunks.push({ start, end, blob });
- start = end;
- end = Math.min(start + chunkSize, totalSize);
- }
- let response: any;
- for (const chunk of chunks) {
- const { start, end, blob } = chunk;
- response = await this.uploadResourceChunk(
- options,
- uniqueUploadId,
- start,
- end - 1,
- totalSize,
- blob,
- );
- }
- return {
- assetId: response.asset_id,
- bytes: response.bytes,
- createdAt: response.created_at,
- format: response.format,
- originalFilename: response.original_filename,
- resourceType: response.resource_type,
- publicId: response.public_id,
- url: response.secure_url,
- };
+ return this.cloudinaryUtils.uploadResourceAsBlob({
+ ...options,
+ cloudName: this.cloudName,
+ blob: options.blob,
+ });
}
public async downloadResource(
options: DownloadResourceOptions,
): Promise {
- const blob = await fetch(options.url).then(res => res.blob());
- return {
- blob,
- };
- }
-
- private async uploadResourceChunk(
- options: UploadResourceOptions,
- uniqueUploadId: string,
- start: number,
- end: number,
- size: number,
- chunk: Blob,
- ): Promise {
- if (!this.cloudName) {
- throw new Error(CloudinaryWeb.ERROR_NOT_INITIALIZED);
- }
- const formData = new FormData();
- formData.append('file', chunk);
- formData.append('upload_preset', options.uploadPreset);
- formData.append('cloud_name', this.cloudName);
- if (options.publicId) {
- formData.append('public_id', options.publicId);
- }
- return fetch(
- `https://api.cloudinary.com/v1_1/${this.cloudName}/${options.resourceType}/upload`,
- {
- method: 'PUT',
- body: formData,
- headers: {
- 'X-Unique-Upload-Id': uniqueUploadId,
- 'Content-Range': `bytes ${start}-${end}/${size}`,
- },
- },
- ).then(async response => {
- if (!response.ok) {
- throw new Error(
- `Request failed with status ${response.status}: ${response.statusText}`,
- );
- }
- return response.json();
- });
- }
-
- private generateUniqueId(): string {
- return Date.now().toString(36) + Math.random().toString(36).substring(2);
+ return this.cloudinaryUtils.downloadResourceAsBlob({ ...options });
}
}