Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/node/src/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export const downloadImageFromURLAsBase64 = async (url: string): Promise<string>

const isBuffer = (file: string | Buffer): file is Buffer => file instanceof Buffer;

const base64DataUriPrefix = /^data:[^,]*;base64,/;

const fileToBase64 = (file: string | Buffer): Promise<string> =>
isFilePromise(file).then((isFile) =>
isFile
Expand All @@ -76,10 +78,12 @@ const fileToBase64 = (file: string | Buffer): Promise<string> =>
/**
* Converts media into a base64 string so that it can be sent to Weaviate.
*
* @param {Media} media The media as a base64 string, a file path, a URL, a `Uint8Array` (including `Buffer`), or a `Blob`. A base64 string is returned as is.
* @param {Media} media The media as a base64 string, a base64 data URI, a file path, a URL, a `Uint8Array` (including `Buffer`), or a `Blob`. A base64 string is returned as is and a data URI has its `data:<mime>;base64,` prefix removed.
* @returns {Promise<string>} The base64 string.
*/
export const toBase64FromMedia = async (media: Media): Promise<string> => {
if (typeof media === 'string' && base64DataUriPrefix.test(media))
return media.replace(base64DataUriPrefix, '');
if (media instanceof Blob) return Buffer.from(await media.arrayBuffer()).toString('base64');
if (media instanceof Uint8Array)
return Buffer.from(media.buffer, media.byteOffset, media.byteLength).toString('base64');
Expand Down
19 changes: 19 additions & 0 deletions packages/test/node/base64/unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { toBase64FromMedia } from '@weaviate/node';
import { describe, expect, it } from 'vitest';

describe('Unit testing of toBase64FromMedia in the node client', () => {
const short = Buffer.from('image bytes').toString('base64');
const long = Buffer.alloc(6000, 7).toString('base64');

it.each([
['a short png data URI', `data:image/png;base64,${short}`, short],
['a long jpeg data URI', `data:image/jpeg;base64,${long}`, long],
['a data URI with media type parameters', `data:image/png;name=img.png;base64,${short}`, short],
])('should strip the prefix from %s', async (_, media, expected) => {
expect(await toBase64FromMedia(media)).toEqual(expected);
});

it('should return a base64 string as is', async () => {
expect(await toBase64FromMedia(long)).toEqual(long);
});
});
17 changes: 17 additions & 0 deletions packages/test/web/base64.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, expect, it } from 'vitest';
import { toBase64FromMedia } from '../../web/src/base64.js';

describe('Unit testing of toBase64FromMedia in the web client', () => {
const b64 = Buffer.from('image bytes').toString('base64');

it.each([
['a png data URI', `data:image/png;base64,${b64}`],
['a data URI with media type parameters', `data:image/png;name=img.png;base64,${b64}`],
])('should strip the prefix from %s', async (_, media) => {
expect(await toBase64FromMedia(media)).toEqual(b64);
});

it('should return a base64 string as is', async () => {
expect(await toBase64FromMedia(b64)).toEqual(b64);
});
});
8 changes: 7 additions & 1 deletion packages/web/src/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const blobToBase64 = (blob: Blob): Promise<string> =>
reader.readAsDataURL(blob);
});

const base64DataUriPrefix = /^data:[^,]*;base64,/;

export const downloadImageFromURLAsBase64 = async (url: string): Promise<string> => {
if (!isUrl(url)) {
throw new Error('Invalid URL');
Expand All @@ -43,13 +45,17 @@ export const downloadImageFromURLAsBase64 = async (url: string): Promise<string>
* Converts media input into a base64 string.
* Accepts:
* - A base64 string (returns it unchanged)
* - A base64 data URI (returns it without the `data:<mime>;base64,` prefix)
* - A URL (fetches the image and converts to base64)
* - A `Blob` or `File` (uses FileReader)
* - A `Uint8Array` (copied into a `Blob`, which rejects `SharedArrayBuffer`-backed views)
*/
export const toBase64FromMedia = (media: Media): Promise<string> => {
if (typeof media === 'string') {
if (media.startsWith('data:') || /^[A-Za-z0-9+/=]+$/.test(media)) {
if (base64DataUriPrefix.test(media)) {
// Base64 data URI, strip the data:mime/type;base64, prefix
return Promise.resolve(media.replace(base64DataUriPrefix, ''));
} else if (media.startsWith('data:') || /^[A-Za-z0-9+/=]+$/.test(media)) {
// Already base64 string
return Promise.resolve(media);
} else if (isUrl(media)) {
Expand Down
Loading