コンテンツにスキップ

ファイル型フィールドを扱う

フィールドの型に ファイル を選ぶと、行にファイル(CSV・画像・PDF など)を添付できます。ファイル本体は Synqlet のストレージに保存され、行にはそのファイル情報が保持されます。

  • 行の作成 / 編集画面でファイルを選択してアップロードします。
  • 行の詳細画面でプレビュー・ダウンロードができます。
  • 行を削除すると、その行に添付されていたファイルも削除されます。

synqlet:database/{テーブルのID} では、ファイル型フィールドの値に次の型を使います。

{
name: string; // ファイル名
mimeType: string; // MIMEタイプ
size: number; // バイト数
url: string; // ダウンロード用の署名付きURL(発行から1時間有効)
}

url はそのまま fetch すればファイルの内容を取得できます。

import * as documents from "synqlet:database/{テーブルのID}";
const row = await documents.get(documents.Id("doc-001"));
const file = row.values.attachment;
if (file) {
const response = await fetch(file.url);
const text = await response.text();
console.log(file.name, text.length);
}

新しいファイルを保存する(FileInput

Section titled “新しいファイルを保存する(FileInput)”

stream / mimeType / name を指定すると、ファイル本体をアップロードして行に添付します。

固定テキストサンプル

import * as table from "synqlet:database/{テーブルのID}";
await table.insertOne({
values: {
title: "日次レポート",
attachment: {
stream: new Blob([
`id,name
1,サンプル`,
]).stream(),
mimeType: "text/csv",
name: "report.csv",
},
},
});

stream には ReadableStream<Uint8Array> を渡します。fetch のレスポンスボディをそのまま渡すこともできます。

const response = await fetch("https://example.com/data.csv");
if (response.body) {
await table.insertOne({
values: {
title: "取り込みデータ",
attachment: {
stream: response.body,
mimeType: "text/csv",
name: "data.csv",
},
},
});
}

ストリーム処理(大きなファイルを1行ずつ変換する)

ファイル全体をメモリに読み込まずに、ダウンロードしたストリームを変換してそのまま stream に渡せます。次は jsr:@std/csv で CSV を1行ずつ読み取り、列を加工しながら保存する例です。

import * as table from "synqlet:database/{テーブルのID}";
import { CsvParseStream, CsvStringifyStream } from "jsr:@std/csv";
export default async function () {
const source = await table.get(table.Id("{行のID}"));
const response = await fetch(source.values.attachment.url);
if (!response.body) {
throw new Error("ファイルの取得に失敗しました");
}
// ヘッダー行を列名として使い、1行ずつオブジェクトとして受け取る
const rowStream = response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(new CsvParseStream({ skipFirstRow: true }));
await table.insertOne({
values: {
title: "取り込みデータ",
attachment: {
stream: ReadableStream.from(
(async function* () {
for await (const row of rowStream) {
yield { name: row.name, per: Number.parseInt(row.score) / 100 };
}
})(),
)
.pipeThrough(new CsvStringifyStream({ columns: ["name", "per"] }))
.pipeThrough(new TextEncoderStream()),
mimeType: "text/csv",
name: "data.csv",
},
},
});
return {};
}

既にあるファイルをコピーする

Section titled “既にあるファイルをコピーする”

取得済みのファイル(FileOutput)をそのまま渡すと、ファイル本体を送り直さずにサーバー側でコピー されます。大きなファイルでも通信量が増えません。

コピー元にできるのは次の2つです。

コピー元
別のテーブル(または同じテーブル)の行のファイルget() / list() で取得した値
フロージョブの添付ファイルフォームトリガーで受け取ったファイル(Parameter の値)
import * as documents from "synqlet:database/{テーブルのID}";
import * as archives from "synqlet:database/{別テーブルのID}";
export default async function (parameter) {
// フォームでアップロードされたファイルをテーブルに保存する
await documents.insertOne({
values: { title: "申請書", attachment: parameter.upload },
});
// 別テーブルの行のファイルをコピーする(名前だけ変更もできる)
const row = await documents.get(documents.Id("doc-001"));
const file = row.values.attachment;
if (file) {
await archives.insertOne({
values: { attachment: { ...file, name: "アーカイブ.csv" } },
});
}
return {};
}

ノードの結果としてファイルを渡す

Section titled “ノードの結果としてファイルを渡す”

ノードの型(Result)にファイル型の項目を定義しておくと、テーブルから取得したファイルを次のノードへ渡せます。この場合もファイル本体は送り直されず、フロージョブの添付ファイルとしてコピーされます。

const row = await documents.get(documents.Id("doc-001"));
return { attachment: row.values.attachment };
  • 行を削除するとファイルも削除されます。行の更新でファイルを差し替えると、参照されなくなった古いファイルは自動的に削除されます。
  • ファイル型フィールドは、画面のフィルターでは絞り込めません。スクリプトや REST API からは name / mimeType / size / id で絞り込めます。
  • ファイル型フィールドでの並び替えはできません。
  • MIMEタイプとファイルサイズは、アップロードされた実際のファイルの情報がサーバー側で記録されます。