File Serving / Download / Export / Archive#

Helper functions for serving files, file downloads, data export, and archive creation. Zero dependencies — uses Bun.Archive (native tar) for archives.

typescript
import {
  serveFileOrFallback,
  downloadFile, downloadBuffer,
  exportCSV, exportJSON,
  createZIP,
} from "@buntok/core";

serveFileOrFallback#

Serve a file from disk. If file doesn't exist, return the fallback response. Useful for serving user uploads with a default fallback (e.g., avatar with initials).

ParameterTypeDescription
ctxContextRequest context
filePathstringPath to the file on disk
fallbackResponse | (() => Response | Promise<Response>)Response to return if file not found
options?{ contentType?, cacheControl? }Response options
typescript
import { serveFileOrFallback, generateInitialAvatar } from "@buntok/core";

// Serve file or return 404
app.get("/documents/:id", async (ctx) => {
  return serveFileOrFallback(ctx, doc.file_path, () => {
    return ctx.json({ error: "Not found" }, 404);
  });
});

// Serve avatar or return default SVG initials
app.get("/avatars/:userId", async (ctx) => {
  const user = await db.users.find(ctx.params.userId);
  return serveFileOrFallback(ctx, user.avatar_path, () => {
    return new Response(generateInitialAvatar(user.name, user.id), {
      headers: { "Content-Type": "image/svg+xml" }
    });
  });
});

// With custom cache control
app.get("/images/:name", async (ctx) => {
  return serveFileOrFallback(ctx, `./uploads/${ctx.params.name}`, () => {
    return new Response("Not found", { status: 404 });
  }, { cacheControl: "public, max-age=3600" });
});
Info
File is served with inline content disposition (displayed in browser). Use downloadFile for forced downloads with attachment disposition.

downloadFile#

Serve a file for download with Content-Disposition: attachment header.

ParameterTypeDescription
ctxContextRequest context
filePathstringPath to the file
filename?stringDownload filename (defaults to basename)
options?{ contentType?, cacheControl? }Response options
typescript
import { downloadFile } from "@buntok/core";

// Serve file as download
app.get("/reports/:id", async (ctx) => {
  return downloadFile(ctx, `./reports/${ctx.params.id}.pdf`);
});

// Custom filename
app.get("/export/users", async (ctx) => {
  return downloadFile(ctx, "./data/users.csv", "users-export.csv");
});

// With options
app.get("/download/:filename", async (ctx) => {
  return downloadFile(ctx, `./files/${ctx.params.filename}`, undefined, {
    cacheControl: "no-cache",
  });
});
Info
Returns 404 if file not found. Content type is auto-detected via Bun.file().

downloadBuffer#

Download a buffer or bytes as a file.

ParameterTypeDescription
ctxContextRequest context
dataArrayBuffer | Uint8Array | BlobFile data to download
filenamestringDownload filename
options?{ contentType? }Response options
typescript
import { downloadBuffer } from "@buntok/core";

// Generate PDF on the fly
app.get("/generate-pdf", async (ctx) => {
  const pdfBytes = await generatePDF(data);
  return downloadBuffer(ctx, pdfBytes, "document.pdf");
});

// From Uint8Array
app.get("/download-binary", async (ctx) => {
  const buffer = new Uint8Array([72, 101, 108, 108, 111]);
  return downloadBuffer(ctx, buffer, "data.bin", {
    contentType: "application/octet-stream",
  });
});

exportCSV#

Export array of objects as CSV file download. Handles commas, quotes, and newlines (RFC 4180 compliant).

ParameterTypeDescription
ctxContextRequest context
dataT[]Array of objects to export
filename?stringDownload filename (default: export.csv)
options?{ delimiter?, header? }CSV options
typescript
import { exportCSV } from "@buntok/core";

// Export users as CSV
app.get("/users/export", async (ctx) => {
  const users = await db.users.find();
  return exportCSV(ctx, users, "users.csv");
});

// Custom delimiter
app.get("/orders/export", async (ctx) => {
  const orders = await db.orders.find();
  return exportCSV(ctx, orders, "orders.tsv", { delimiter: "\t" });
});

// Without header row
app.get("/data/export", async (ctx) => {
  return exportCSV(ctx, rows, "data.csv", { header: false });
});
Info
Auto-escapes values containing commas, quotes, or newlines. Nested objects are flattened with dot notation (e.g., user.name).

exportJSON#

Export data as JSON file download.

typescript
import { exportJSON } from "@buntok/core";

app.get("/data/export", async (ctx) => {
  const data = await db.orders.find();
  return exportJSON(ctx, data, "orders.json");
});

createZIP#

Create a tar archive from multiple files and serve as download. Uses Bun.Archive (native).

ParameterTypeDescription
ctxContextRequest context
filesZIPEntry[]Files to include ({ name, data })
filename?stringDownload filename (default: archive.tar)
options?{ compress? }Enable gzip compression
typescript
import { createZIP, exportCSV, exportJSON } from "@buntok/core";

// Export multiple files as archive
app.get("/export/bundle", async (ctx) => {
  const users = await db.users.find();
  const orders = await db.orders.find();

  return createZIP(ctx, [
    { name: "users.csv", data: exportCSVToString(users) },
    { name: "orders.json", data: JSON.stringify(orders) },
  ], "export.tar");
});

// With gzip compression
app.get("/export/compressed", async (ctx) => {
  const data = await db.exports.find();
  return createZIP(ctx, [
    { name: "data.json", data: JSON.stringify(data) },
  ], "export.tar.gz", { compress: true });
});
Info
Uses tar format (not zip). For .tar.gz, pass { compress: true }.