Open
Description
Hi!
First thanks for the great work, especially for the effort in types!
As a lot of the community uses S3 for storage, I was wondering if you would be
interested in supporting a dedicated S3 Store (building on the HTTP Store but with
support for accessKeys and secretKeys? I could spin up a PR, but here is already an
implementation using "aws4fetch".
import {
HTTPStore,
openGroup,
openArray,
ZarrArray,
Group as ZarrGroup,
} from "zarr";
import { AwsClient } from "aws4fetch";
enum HTTPMethod {
Get = "GET",
Head = "HEAD",
Put = "PUT",
}
export class S3HttpError extends Error {
__zarr__: string;
constructor(code: any) {
super(code);
this.__zarr__ = "HTTPError";
Object.setPrototypeOf(this, HTTPError.prototype);
}
}
export class S3KeyError extends Error {
__zarr__: string;
constructor(key: any) {
super(`key ${key} not present`);
this.__zarr__ = "KeyError";
Object.setPrototypeOf(this, KeyError.prototype);
}
}
export function joinUrlParts(...args: string[]) {
return args
.map((part, i) => {
if (i === 0) {
return part.trim().replace(/[\/]*$/g, "");
} else {
return part.trim().replace(/(^[\/]*|[\/]*$)/g, "");
}
})
.filter((x) => x.length)
.join("/");
}
class S3Store extends HTTPStore {
aws: AwsClient;
constructor(url: string, aws: AwsClient, options: any = {}) {
super(url, options);
this.aws = aws;
}
async getItem(item: any, opts: any) {
const url = joinUrlParts(this.url, item);
let value: any;
try {
value = await this.aws.fetch(url, { ...this.fetchOptions, ...opts });
} catch (e) {
throw new S3HTTPError("present");
}
if (value.status === 404) {
// Item is not found
throw new S3KeyError(item);
} else if (value.status !== 200) {
throw new S3HttpError(String(value.status));
}
return value.arrayBuffer(); // Browser
// only decode if 200
}
async setItem(item: any, value: any) {
const url = joinUrlParts(this.url, item);
if (typeof value === "string") {
value = new TextEncoder().encode(value).buffer;
}
const set = await this.aws.fetch(url, {
...this.fetchOptions,
method: HTTPMethod.Put,
body: value,
});
return set.status.toString()[0] === "2";
}
async containsItem(item: any) {
const url = joinUrlParts(this.url, item);
try {
const value = await this.aws.fetch(url, {
...this.fetchOptions,
});
return value.status === 200;
} catch (e) {
return false;
}
}
}
Hope that might help some!
Metadata
Assignees
Labels
No labels