or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

access-control.mdauthentication.mdbucket-operations.mdfile-operations.mdindex.mdnotifications.mdstorage-client.mdtransfer-manager.mdutilities.md
tile.json

file-operations.mddocs/

File Operations

The File class represents a Cloud Storage object/file with comprehensive methods for upload, download, management, encryption, and access control.

Class Definition

class File extends ServiceObject {
  constructor(bucket: Bucket, name: string, options?: FileOptions);
  
  // Properties
  name: string;
  bucket: Bucket;
  storage: Storage;
  acl: Acl;
  generation?: number;
  kmsKeyName?: string;
  userProject?: string;
  
  // Upload/Download methods
  save(data: SaveData, options?: SaveOptions): Promise<void>;
  createWriteStream(options?: CreateWriteStreamOptions): Writable;
  createResumableUpload(options?: CreateResumableUploadOptions): Promise<CreateResumableUploadResponse>;
  download(options?: DownloadOptions): Promise<DownloadResponse>;
  createReadStream(options?: CreateReadStreamOptions): Readable;
  
  // File management methods
  exists(options?: FileExistsOptions): Promise<FileExistsResponse>;
  get(options?: GetFileOptions): Promise<GetFileResponse>;
  getMetadata(options?: GetFileMetadataOptions): Promise<GetFileMetadataResponse>;
  setMetadata(metadata: FileMetadata, options?: SetFileMetadataOptions): Promise<SetFileMetadataResponse>;
  copy(destination: string | Bucket | File, options?: CopyOptions): Promise<CopyResponse>;
  move(destination: string | Bucket | File, options?: MoveOptions): Promise<MoveResponse>;
  delete(options?: DeleteFileOptions): Promise<DeleteFileResponse>;
}

interface FileOptions {
  encryptionKey?: string | Buffer;
  generation?: number | string;
  kmsKeyName?: string;
  userProject?: string;
}

Upload Operations

Save Data to File

save(data: SaveData, options?: SaveOptions): Promise<void>

type SaveData = string | Buffer | Uint8Array;

interface SaveOptions {
  encryptionKey?: string | Buffer;
  gzip?: boolean;
  metadata?: FileMetadata;
  offset?: number;
  predefinedAcl?: PredefinedAcl;
  private?: boolean;
  public?: boolean;
  resumable?: boolean;
  timeout?: number;
  uri?: string;
  userProject?: string;
  validation?: string | boolean;
  preconditionOpts?: PreconditionOptions;
}

// Save string data
await file.save('Hello, World!');

// Save buffer data
const buffer = Buffer.from('Binary data', 'utf8');
await file.save(buffer);

// Save with metadata
await file.save('File content', {
  metadata: {
    contentType: 'text/plain',
    cacheControl: 'no-cache',
    metadata: {
      author: 'user-123',
      version: '1.0'
    }
  }
});

// Save with compression
await file.save('Large text content...', {
  gzip: true,
  metadata: {
    contentEncoding: 'gzip'
  }
});

// Save with encryption
await file.save('Secret data', {
  encryptionKey: crypto.randomBytes(32).toString('base64')
});

// Save with access control
await file.save('Public content', {
  public: true
});

Create Write Stream

createWriteStream(options?: CreateWriteStreamOptions): Writable

interface CreateWriteStreamOptions {
  chunkSize?: number;
  encryptionKey?: string | Buffer;
  gzip?: boolean;
  metadata?: FileMetadata;
  offset?: number;
  predefinedAcl?: PredefinedAcl;
  private?: boolean;
  public?: boolean;
  resumable?: boolean;
  timeout?: number;
  uri?: string;
  userProject?: string;
  validation?: string | boolean;
  preconditionOpts?: PreconditionOptions;
}

// Basic write stream
const writeStream = file.createWriteStream();
writeStream.write('First chunk\n');
writeStream.write('Second chunk\n');
writeStream.end();

// Stream from file with metadata
const writeStream = file.createWriteStream({
  metadata: {
    contentType: 'application/json'
  }
});

fs.createReadStream('/local/file.json').pipe(writeStream);

// Resumable upload for large files
const writeStream = file.createWriteStream({
  resumable: true,
  chunkSize: 256 * 1024, // 256KB chunks
  metadata: {
    contentType: 'video/mp4'
  }
});

writeStream.on('error', (err) => {
  console.error('Upload failed:', err);
});

writeStream.on('finish', () => {
  console.log('Upload completed');
});

// Compressed upload
const writeStream = file.createWriteStream({
  gzip: true,
  metadata: {
    contentEncoding: 'gzip'
  }
});

// Pipeline with transformation
const zlib = require('zlib');
fs.createReadStream('/large/file.txt')
  .pipe(zlib.createGzip())
  .pipe(writeStream);

Create Resumable Upload

createResumableUpload(options?: CreateResumableUploadOptions): Promise<CreateResumableUploadResponse>

interface CreateResumableUploadOptions {
  chunkSize?: number;
  configPath?: string;
  encryptionKey?: string | Buffer;
  generation?: number;
  kmsKeyName?: string;
  metadata?: FileMetadata;
  offset?: number;
  origin?: string;
  predefinedAcl?: PredefinedAcl;
  private?: boolean;
  public?: boolean;
  uri?: string;
  userProject?: string;
  preconditionOpts?: PreconditionOptions;
}

type CreateResumableUploadResponse = [string]; // [resumeUri]

// Create resumable upload session
const [resumeUri] = await file.createResumableUpload({
  chunkSize: 1024 * 1024, // 1MB chunks
  metadata: {
    contentType: 'application/octet-stream'
  }
});

console.log('Resume URI:', resumeUri);

// Resume upload later
const writeStream = file.createWriteStream({
  uri: resumeUri
});

Download Operations

Download to Memory

download(options?: DownloadOptions): Promise<DownloadResponse>

interface DownloadOptions {
  destination?: string;
  start?: number;
  end?: number;
  validation?: string | boolean;
  userProject?: string;
  decompress?: boolean;
}

type DownloadResponse = [Buffer]; // [fileContents]

// Download entire file
const [contents] = await file.download();
console.log('File contents:', contents.toString());

// Download to local file
await file.download({
  destination: '/local/path/downloaded-file.txt'
});

// Download byte range
const [partialContents] = await file.download({
  start: 0,
  end: 1023 // First 1KB
});

// Download with decompression
const [contents] = await file.download({
  decompress: true // Auto-decompress gzipped files
});

// Download without validation (faster)
const [contents] = await file.download({
  validation: false
});

Create Read Stream

createReadStream(options?: CreateReadStreamOptions): Readable

interface CreateReadStreamOptions {
  start?: number;
  end?: number;
  validation?: string | boolean;
  userProject?: string;
  decompress?: boolean;
}

// Basic read stream
const readStream = file.createReadStream();
readStream.pipe(fs.createWriteStream('/local/output.txt'));

// Stream byte range
const readStream = file.createReadStream({
  start: 1024,
  end: 2047 // Bytes 1024-2047
});

// Stream with decompression
const readStream = file.createReadStream({
  decompress: true
});

// Stream processing
file.createReadStream()
  .on('data', (chunk) => {
    console.log(`Received ${chunk.length} bytes`);
  })
  .on('end', () => {
    console.log('Download completed');
  })
  .on('error', (err) => {
    console.error('Download failed:', err);
  });

// Transform stream
const zlib = require('zlib');
file.createReadStream()
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream('/local/uncompressed.txt'));

File Management

Check File Existence

exists(options?: FileExistsOptions): Promise<FileExistsResponse>

interface FileExistsOptions {
  userProject?: string;
}

type FileExistsResponse = [boolean]; // [exists]

// Check if file exists
const [exists] = await file.exists();
if (exists) {
  console.log('File exists');
} else {
  console.log('File does not exist');
}

Get File (Create if Needed)

get(options?: GetFileOptions): Promise<GetFileResponse>

interface GetFileOptions extends GetFileMetadataOptions {
  autoCreate?: boolean;
  generation?: number;
}

type GetFileResponse = [File, unknown]; // [file, apiResponse]

// Get file (create empty file if doesn't exist)
const [file] = await file.get({
  autoCreate: true
});

File Metadata

getMetadata(options?: GetFileMetadataOptions): Promise<GetFileMetadataResponse>
setMetadata(metadata: FileMetadata, options?: SetFileMetadataOptions): Promise<SetFileMetadataResponse>

interface GetFileMetadataOptions {
  userProject?: string;
  generation?: number;
  ifGenerationMatch?: number;
  ifGenerationNotMatch?: number;
  ifMetagenerationMatch?: number;
  ifMetagenerationNotMatch?: number;
}

interface FileMetadata {
  acl?: AclMetadata[];
  bucket?: string;
  cacheControl?: string;
  componentCount?: number;
  contentDisposition?: string;
  contentEncoding?: string;
  contentLanguage?: string;
  contentType?: string;
  crc32c?: string;
  customTime?: string;
  customerEncryption?: {
    encryptionAlgorithm?: string;
    keySha256?: string;
  };
  etag?: string;
  eventBasedHold?: boolean;
  generation?: string;
  id?: string;
  kmsKeyName?: string;
  md5Hash?: string;
  mediaLink?: string;
  metadata?: { [key: string]: string };
  metageneration?: string;
  name?: string;
  owner?: {
    entity?: string;
    entityId?: string;
  };
  retentionExpirationTime?: string;
  selfLink?: string;
  size?: string;
  storageClass?: string;
  temporaryHold?: boolean;
  timeCreated?: string;
  timeDeleted?: string;
  updated?: string;
}

type GetFileMetadataResponse = [FileMetadata, unknown]; // [metadata, apiResponse]
type SetFileMetadataResponse = [FileMetadata, unknown]; // [metadata, apiResponse]

// Get file metadata
const [metadata] = await file.getMetadata();
console.log('File size:', metadata.size);
console.log('Content type:', metadata.contentType);
console.log('Created:', metadata.timeCreated);
console.log('MD5 hash:', metadata.md5Hash);

// Set custom metadata
await file.setMetadata({
  metadata: {
    author: 'john-doe',
    version: '2.1',
    category: 'document'
  }
});

// Update content headers
await file.setMetadata({
  contentType: 'application/pdf',
  contentDisposition: 'attachment; filename="report.pdf"',
  cacheControl: 'public, max-age=3600'
});

// Set custom time (for lifecycle rules)
await file.setMetadata({
  customTime: '2023-12-31T23:59:59Z'
});

// Set holds (for retention)
await file.setMetadata({
  temporaryHold: true,
  eventBasedHold: false
});

File Operations

Copy File

copy(destination: string | Bucket | File, options?: CopyOptions): Promise<CopyResponse>

interface CopyOptions {
  destinationKmsKeyName?: string;
  predefinedAcl?: PredefinedAcl;
  token?: string;
  userProject?: string;
  preconditionOpts?: PreconditionOptions;
}

type CopyResponse = [File, unknown]; // [destinationFile, apiResponse]

// Copy within same bucket
const [copiedFile] = await file.copy('backup/copy-of-file.txt');

// Copy to different bucket
const destinationBucket = storage.bucket('other-bucket');
const [copiedFile] = await file.copy(destinationBucket.file('copied-file.txt'));

// Copy with different encryption
const [copiedFile] = await file.copy('encrypted-copy.txt', {
  destinationKmsKeyName: 'projects/PROJECT_ID/locations/us/keyRings/ring/cryptoKeys/key'
});

// Copy with ACL
const [copiedFile] = await file.copy('public-copy.txt', {
  predefinedAcl: 'publicRead'
});

Move File

move(destination: string | Bucket | File, options?: MoveOptions): Promise<MoveResponse>

interface MoveOptions {
  destinationKmsKeyName?: string;
  predefinedAcl?: PredefinedAcl;
  userProject?: string;
  preconditionOpts?: PreconditionOptions;
}

type MoveResponse = [File, unknown]; // [destinationFile, apiResponse]

// Move within same bucket
const [movedFile] = await file.move('new-location/file.txt');

// Move to different bucket
const destinationBucket = storage.bucket('archive-bucket');
const [movedFile] = await file.move(destinationBucket.file('archived-file.txt'));

// Move with encryption change
const [movedFile] = await file.move('encrypted/file.txt', {
  destinationKmsKeyName: 'projects/PROJECT_ID/locations/us/keyRings/ring/cryptoKeys/key'
});

Delete File

delete(options?: DeleteFileOptions): Promise<DeleteFileResponse>

interface DeleteFileOptions {
  ignoreNotFound?: boolean;
  userProject?: string;
  ifGenerationMatch?: number;
  ifGenerationNotMatch?: number;
  ifMetagenerationMatch?: number;
  ifMetagenerationNotMatch?: number;
}

type DeleteFileResponse = [unknown]; // [apiResponse]

// Delete file
await file.delete();

// Delete specific generation
await file.delete({
  ifGenerationMatch: 1234567890
});

// Delete ignoring not found errors
await file.delete({
  ignoreNotFound: true
});

Access Control

Make Public/Private

makePublic(): Promise<MakeFilePublicResponse>
makePrivate(options?: MakeFilePrivateOptions): Promise<MakeFilePrivateResponse>

interface MakeFilePrivateOptions {
  strict?: boolean;
  userProject?: string;
}

type MakeFilePublicResponse = [unknown]; // [apiResponse]
type MakeFilePrivateResponse = [unknown]; // [apiResponse]

// Make file publicly readable
await file.makePublic();

// Make file private
await file.makePrivate();

// Strict private (remove all access)
await file.makePrivate({
  strict: true
});

// Check if file is public
const [isPublic] = await file.isPublic();
console.log('File is public:', isPublic);

Storage Class

setStorageClass(storageClass: string, options?: SetStorageClassOptions): Promise<SetStorageClassResponse>

interface SetStorageClassOptions {
  userProject?: string;
  ifGenerationMatch?: number;
  ifGenerationNotMatch?: number;
  ifMetagenerationMatch?: number;
  ifMetagenerationNotMatch?: number;
}

type SetStorageClassResponse = [unknown]; // [apiResponse]

// Available storage classes
const storageClasses = [
  'STANDARD',
  'NEARLINE',
  'COLDLINE', 
  'ARCHIVE',
  'DURABLE_REDUCED_AVAILABILITY'
];

// Change storage class
await file.setStorageClass('COLDLINE');

// Change with precondition
await file.setStorageClass('ARCHIVE', {
  ifGenerationMatch: file.generation
});

Security and Encryption

Rotate Encryption Key

rotateEncryptionKey(encryptionKey?: string | Buffer, options?: RotateEncryptionKeyOptions): Promise<RotateEncryptionKeyResponse>

interface RotateEncryptionKeyOptions {
  kmsKeyName?: string;
  userProject?: string;
}

type RotateEncryptionKeyResponse = [File, unknown]; // [file, apiResponse]

// Generate new encryption key
const newKey = crypto.randomBytes(32).toString('base64');
const [file] = await file.rotateEncryptionKey(newKey);

// Rotate to KMS key
const [file] = await file.rotateEncryptionKey(undefined, {
  kmsKeyName: 'projects/PROJECT_ID/locations/us/keyRings/ring/cryptoKeys/key'
});

// Remove encryption (make unencrypted)
const [file] = await file.rotateEncryptionKey();

Signed URLs

getSignedUrl(config: GetSignedUrlConfig): Promise<GetSignedUrlResponse>

interface GetSignedUrlConfig {
  version: 'v2' | 'v4';
  action: 'read' | 'write' | 'delete' | 'resumable';
  expires: string | number | Date;
  accessibleAt?: string | number | Date;
  contentMd5?: string;
  contentType?: string;
  extensionHeaders?: { [key: string]: string };
  promptSaveAs?: string;
  queryParams?: { [key: string]: string };
  responseDisposition?: string;
  responseType?: string;
  virtualHostedStyle?: boolean;
  cname?: string;
}

type GetSignedUrlResponse = [string]; // [signedUrl]

// Read access (download)
const [url] = await file.getSignedUrl({
  version: 'v4',
  action: 'read',
  expires: Date.now() + 15 * 60 * 1000 // 15 minutes
});

// Write access (upload)
const [url] = await file.getSignedUrl({
  version: 'v4',
  action: 'write',
  expires: Date.now() + 60 * 60 * 1000, // 1 hour
  contentType: 'image/jpeg'
});

// With custom response headers
const [url] = await file.getSignedUrl({
  version: 'v4',
  action: 'read',
  expires: Date.now() + 3600000,
  responseType: 'application/pdf',
  responseDisposition: 'attachment; filename="document.pdf"'
});

// Resumable upload
const [url] = await file.getSignedUrl({
  version: 'v4',
  action: 'resumable',
  expires: Date.now() + 24 * 60 * 60 * 1000 // 24 hours
});

// With query parameters
const [url] = await file.getSignedUrl({
  version: 'v4',
  action: 'read',
  expires: Date.now() + 3600000,
  queryParams: {
    'response-cache-control': 'no-cache'
  }
});

Signed POST Policies

generateSignedPostPolicyV2(options: GenerateSignedPostPolicyV2Options): Promise<GenerateSignedPostPolicyV2Response>
generateSignedPostPolicyV4(options: GenerateSignedPostPolicyV4Options): Promise<GenerateSignedPostPolicyV4Response>

interface GenerateSignedPostPolicyV2Options {
  expires: string | number | Date;
  equals?: string[][];
  startsWith?: string[][];
  acl?: string;
  successRedirect?: string;
  successStatus?: string;
  contentLengthRange?: {
    min?: number;
    max?: number;
  };
}

interface GenerateSignedPostPolicyV4Options {
  expires: string | number | Date;
  conditions?: (string | string[])[];
  fields?: { [key: string]: string };
  virtualHostedStyle?: boolean;
  bucketBoundHostname?: string;
}

interface PolicyDocument {
  expiration: string;
  conditions: (string | string[] | { [key: string]: string })[];
}

interface PolicyFields {
  [key: string]: string;
}

interface SignedPostPolicyV4Output {
  url: string;
  fields: PolicyFields;
}

type GenerateSignedPostPolicyV2Response = [PolicyDocument, string]; // [policy, signature]
type GenerateSignedPostPolicyV4Response = [SignedPostPolicyV4Output]; // [signedPostPolicy]

// V2 POST policy
const [policy, signature] = await file.generateSignedPostPolicyV2({
  expires: Date.now() + 60 * 60 * 1000, // 1 hour
  contentLengthRange: {
    min: 1,
    max: 10 * 1024 * 1024 // 10MB
  }
});

// V4 POST policy (recommended)
const [signedPostPolicy] = await file.generateSignedPostPolicyV4({
  expires: Date.now() + 60 * 60 * 1000,
  conditions: [
    ['content-length-range', 1, 10 * 1024 * 1024],
    ['starts-with', '$content-type', 'image/']
  ],
  fields: {
    'x-goog-meta-user': 'user-123'
  }
});

console.log('POST URL:', signedPostPolicy.url);
console.log('Form fields:', signedPostPolicy.fields);

Utility Methods

Get Expiration Date

getExpirationDate(): Promise<GetExpirationDateResponse>

type GetExpirationDateResponse = [Date]; // [expirationDate]

// Get file expiration date (from lifecycle rules)
const [expirationDate] = await file.getExpirationDate();
if (expirationDate) {
  console.log('File expires on:', expirationDate);
} else {
  console.log('File does not have an expiration date');
}

Check Public Access

isPublic(): Promise<[boolean]>

// Check if file is publicly accessible
const [isPublic] = await file.isPublic();
console.log('File is public:', isPublic);

Predefined ACLs

type PredefinedAcl = 
  | 'authenticatedRead'
  | 'bucketOwnerFullControl'
  | 'bucketOwnerRead'
  | 'private'
  | 'projectPrivate'
  | 'publicRead'
  | 'publicReadWrite';

// Usage in upload operations
await file.save('content', {
  predefinedAcl: 'publicRead'
});

// Usage in copy operations
await file.copy('new-file.txt', {
  predefinedAcl: 'bucketOwnerFullControl'
});

Error Handling

import { ApiError } from '@google-cloud/storage';

try {
  const [contents] = await file.download();
} catch (error) {
  if (error instanceof ApiError) {
    if (error.code === 404) {
      console.log('File not found');
    } else if (error.code === 403) {
      console.log('Access denied');
    } else {
      console.error(`API Error ${error.code}: ${error.message}`);
    }
  }
}

Callback Support

All async methods support both Promise and callback patterns:

// Promise pattern (recommended)
const [contents] = await file.download();

// Callback pattern
file.download((err, contents) => {
  if (err) {
    console.error('Error:', err);
    return;
  }
  console.log('Downloaded file contents');
});

// Callback types
interface DownloadCallback {
  (err: Error | null, contents?: Buffer): void;
}

interface CopyCallback {
  (err: Error | null, copiedFile?: File, apiResponse?: unknown): void;
}

interface DeleteFileCallback {
  (err: Error | null, apiResponse?: unknown): void;
}