@flystorage/google-cloud-storage
Version:
<img src="https://raw.githubusercontent.com/duna-oss/flystorage/main/flystorage.svg" width="50px" height="50px" />
193 lines (192 loc) • 7.54 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleCloudFileStorage = exports.GoogleCloudStorageAdapter = void 0;
const file_storage_1 = require("@flystorage/file-storage");
const stream_mime_type_1 = require("@flystorage/stream-mime-type");
const promises_1 = require("node:stream/promises");
const visibility_handling_js_1 = require("./visibility-handling.js");
class GoogleCloudStorageAdapter {
bucket;
options;
visibilityHandling;
prefixer;
constructor(bucket, options = {}, visibilityHandling = new visibility_handling_js_1.UniformBucketLevelAccessVisibilityHandling()) {
this.bucket = bucket;
this.options = options;
this.visibilityHandling = visibilityHandling;
this.prefixer = new file_storage_1.PathPrefixer(options.prefix ?? '');
}
async write(path, contents, options) {
let mimeType = options.mimeType;
if (mimeType === undefined) {
[mimeType, contents] = await (0, stream_mime_type_1.resolveMimeType)(path, contents);
}
const writeStream = this.bucket.file(this.prefixer.prefixFilePath(path))
.createWriteStream({
contentType: mimeType,
predefinedAcl: options.visibility
? this.visibilityHandling.visibilityToPredefinedAcl(options.visibility)
: undefined,
metadata: options.cacheControl ? { cacheControl: options.cacheControl } : undefined,
});
await (0, promises_1.pipeline)(contents, writeStream);
}
async read(path) {
const readStream = this.bucket.file(this.prefixer.prefixFilePath(path)).createReadStream();
// force retrieval of the head to ensure the http call is made
// this ensures the error from the HTTP call is caught at the
// abstraction level
const [_, outStream] = await (0, stream_mime_type_1.streamHead)(readStream, 10);
return outStream;
}
async deleteFile(path) {
await this.bucket.file(this.prefixer.prefixFilePath(path)).delete({
ignoreNotFound: true,
});
}
async createDirectory(path, options) {
await this.bucket.file(this.prefixer.prefixDirectoryPath(path)).save('');
}
async copyFile(from, to, options) {
await this.bucket.file(this.prefixer.prefixFilePath(from)).copy(this.bucket.file(this.prefixer.prefixFilePath(to)));
}
async moveFile(from, to, options) {
await this.copyFile(from, to, {});
await this.deleteFile(from);
}
async stat(path) {
const [metadata] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getMetadata();
return this.mapToStatEntry(metadata);
}
async *list(path, options) {
let response;
let query = {
autoPaginate: false,
delimiter: options.deep ? undefined : '/',
includeTrailingDelimiter: options.deep ? undefined : true,
prefix: this.prefixer.prefixDirectoryPath(path),
};
while (query !== null) {
[response, query] = await this.bucket.getFiles(query);
for (const item of response) {
yield this.mapToStatEntry(item.metadata);
}
}
}
mapToStatEntry(file) {
if (file.name.endsWith('/')) {
return {
type: 'directory',
isFile: false,
isDirectory: true,
path: this.prefixer.stripDirectoryPath(file.name),
};
}
return {
type: 'file',
isFile: true,
isDirectory: false,
path: this.prefixer.stripFilePath(file.name),
lastModifiedMs: file.updated ? new Date(file.updated).getTime() : undefined,
mimeType: file.contentType,
};
}
async changeVisibility(path, visibility) {
await this.visibilityHandling.changeVisibility(this.bucket.file(this.prefixer.prefixFilePath(path)), visibility);
}
async visibility(path) {
return await this.visibilityHandling.determineVisibility(this.bucket.file(this.prefixer.prefixFilePath(path)));
}
async deleteDirectory(path) {
const prefix = this.prefixer.prefixDirectoryPath(path);
await this.bucket.deleteFiles({
prefix,
});
await this.bucket.file(prefix).delete({
ignoreNotFound: true,
});
}
async fileExists(path) {
const [exists] = await this.bucket.file(this.prefixer.prefixFilePath(path)).exists();
return exists;
}
async directoryExists(path) {
const [exists] = await this.bucket.file(this.prefixer.prefixDirectoryPath(path)).exists();
if (exists) {
return true;
}
const [response] = await this.bucket.getFiles({
autoPaginate: false,
maxResults: 1,
prefix: this.prefixer.prefixDirectoryPath(path),
});
return response.length > 0;
}
async publicUrl(path, options) {
return this.bucket.file(this.prefixer.prefixFilePath(path)).publicUrl();
}
async temporaryUrl(path, options) {
const [response] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getSignedUrl({
action: 'read',
expires: options.expiresAt,
});
return response;
}
async prepareUpload(path, options) {
const headers = {};
const config = {
action: 'write',
expires: options.expiresAt,
};
const contentType = options['Content-Type'] ?? options.contentType;
if (typeof contentType === 'string') {
config.contentType = contentType;
headers['Content-Type'] = contentType;
}
const [url] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getSignedUrl(config);
return {
url,
headers,
method: 'PUT',
provider: 'google-cloud-storage',
};
}
async checksum(path, options) {
const algo = options.algo ?? 'md5';
if (algo !== 'md5' && algo !== 'crc32c') {
throw file_storage_1.ChecksumIsNotAvailable.checksumNotSupported(algo);
}
const [metadata] = await this.bucket.file(this.prefixer.prefixFilePath(path)).getMetadata();
return algo === 'crc32c' ? metadata.crc32c : metadata.md5Hash;
}
async mimeType(path, options) {
const stat = await this.stat(path);
if (stat.type !== 'file' || stat.mimeType === undefined) {
throw new Error('Unable to resolve mime-type, not available in stat entry.');
}
return stat.mimeType;
}
async lastModified(path) {
const stat = await this.stat(path);
if (stat.type !== 'file' || stat.lastModifiedMs === undefined) {
throw new Error('Unable to resolve last modified time, not available in stat entry.');
}
return stat.lastModifiedMs;
}
async fileSize(path) {
const stat = await this.stat(path);
if (stat.type !== 'file' || stat.size === undefined) {
throw new Error('Unable to resolve file size, not available in stat entry.');
}
return stat.size;
}
}
exports.GoogleCloudStorageAdapter = GoogleCloudStorageAdapter;
/**
* BC export
*
* @deprecated
*/
class GoogleCloudFileStorage extends GoogleCloudStorageAdapter {
}
exports.GoogleCloudFileStorage = GoogleCloudFileStorage;