ファイルをコピー・複製する

Recipe ID: fs-011

ファイルを指定した場所にコピー(複製)する方法を紹介します。
アプリの初期化時に、バンドルされたデフォルト設定ファイルをローカルデータフォルダに展開する場合などに役立ちます。

前提条件

このレシピを使用するには、@tauri-apps/plugin-fs プラグインが必要です。

1. プラグインのインストール

プロジェクトのルートディレクトリで以下のコマンドを実行してプラグインを追加します。

npm run tauri add fs

2. Permissions (権限) の設定

src-tauri/capabilities/default.json に以下の権限を追加します。

{
  "permissions": [
    ...,
    {
      "identifier": "fs:allow-copy-file",
      "allow": [{ "path": "$RESOURCE/**" }, { "path": "$APPLOCALDATA/**" }]
    },
    {
      "identifier": "fs:allow-exists",
      "allow": [{ "path": "$APPLOCALDATA/**" }]
    }
  ]
}

※ 上記の例では $RESOURCEBaseDirectory.Resource)および $APPLOCALDATABaseDirectory.AppLocalData)配下でのコピー操作、$APPLOCALDATA での存在確認を許可しています。アクセスするディレクトリに応じて、パス変数を指定してください。スコープが設定されていないディレクトリにはアクセスできません。

1. フロントエンドから作成する (TypeScript)

@tauri-apps/plugin-fscopyFile 関数を使用します。

初期設定ファイルの展開

アプリに同梱されたデフォルトの設定ファイルを、ユーザー書き込み可能な領域にコピーする典型的なパターンです。

import { copyFile, BaseDirectory, exists } from '@tauri-apps/plugin-fs';

// 事前に src-tauri/tauri.conf.json の bundle > resources に
// "assets/*" などを追加し、ファイルをビルドに含める必要があります。
async function initConfigFile() {
  try {
    // 既に設定ファイルが存在するか確認
    const fileExists = await exists('config.json', {
      baseDir: BaseDirectory.AppLocalData,
    });

    if (!fileExists) {
      // 存在しない場合、リソースからコピーして作成
      // "assets/*" と設定した場合、ファイルは Resource ディレクトリの直下に配置されます
      await copyFile('default_config.json', 'config.json', {
        fromPathBaseDir: BaseDirectory.Resource,
        toPathBaseDir: BaseDirectory.AppLocalData,
      });
      console.log('初期設定ファイルを生成しました');
    } else {
      console.log('設定ファイルは既に存在します');
    }
  } catch (err) {
    console.error('初期化エラー:', err);
  }
}

リソースの設定について

上記のコードで BaseDirectory.Resource にある default_config.json (元は assets/default_config.json)にアクセスするには、src-tauri/tauri.conf.json でリソースファイルの同梱設定を行う必要があります。

src-tauri/tauri.conf.jsonsrc-tauri ディレクトリ内にあるため、プロジェクトルートにある assets フォルダを指定する場合は ../assets/* となります。

// src-tauri/tauri.conf.json
{
  "bundle": {
    "resources": [
      // src-tauri フォルダ内に assets フォルダがある場合の例
      // プロジェクトルートにある場合は "../assets/*" と指定します
      "assets/*"
    ]
  }
}

"assets/*" のようにワイルドカード指定した場合、ファイル構成はフラット化(または直下に配置)されるため、コード内からは assets/ などのディレクトリ名を含めずにファイル名だけでアクセスします。

単純なバックアップの作成

既存のファイルを別名で保存(バックアップ)します。

import { copyFile, BaseDirectory } from '@tauri-apps/plugin-fs';

async function backupData() {
  try {
    const timestamp = new Date().getTime();
    const backupName = `data_${timestamp}.bak`;

    await copyFile('user_data.db', backupName, {
      fromPathBaseDir: BaseDirectory.AppLocalData,
      toPathBaseDir: BaseDirectory.AppLocalData, // 同じフォルダ内でコピー
    });
    console.log(`バックアップを作成しました: ${backupName}`);
  } catch (err) {
    console.error('バックアップ作成エラー:', err);
  }
}

2. バックエンドから作成する (Rust)

Rust の std::fs を使用します。

ファイルコピー

use std::fs;

#[tauri::command]
fn copy_file(src: String, dst: String) -> Result<(), String> {
    // 上書きされる点に注意してください
    fs::copy(src, dst)
        .map(|_| ())
        .map_err(|e| e.to_string())
}

バックアップ作成

use std::fs;
use std::path::Path;

#[tauri::command]
fn create_backup(path: String) -> Result<String, String> {
    let src = Path::new(&path);
    if !src.exists() {
        return Err("Source file does not exist".to_string());
    }

    let dest = format!("{}.bak", path);
    fs::copy(src, &dest).map_err(|e| e.to_string())?;
    
    Ok(dest)
}

補足

  • copyFile: Rust では std::fs::copy を使用します。
  • 上書き: コピー先にファイルが存在する場合、JS の copyFile も Rust の fs::copy も基本的に内容を上書きします。