You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tauri 1.0 JavaScript应用播放本地音频片段的问题求助

Tauri 1.0 内嵌音频播放问题解决指南

用户问题场景

我正在开发首个基于JavaScript的Tauri 1.0应用,功能是对接条形码扫描器、向API提交信息,并根据API响应显示成功/失败状态。为优化用户体验,我想播放应用包内的小型音频片段,但尝试后遇到了问题,想请教有经验的开发者:

  1. 音频文件该放在哪里?是src还是src-tauri目录下?
  2. 如何在main.ts中正确引用音频文件?
  3. 需要修改哪些配置才能实现这个功能?
  4. 音频是在API响应成功/失败时自动播放,不是用户触发(比如点击),会不会有问题?

已尝试的方法及报错

方法一:直接使用相对路径

const playAudio = (path: string) => {
  const audio = new Audio(path);
  audio.play().catch(error => {
    console.error('Playback failed:', error);
  });
};
playAudio('/assets/audio/success.mp3');

报错:NotSupportedError: The operation is not supported.

方法二:使用convertFileSrc转换绝对路径

function playAudio(absoluteFilePath: string) {
  const assetUrl = convertFileSrc(absoluteFilePath);
  const audio = new Audio(assetUrl);
  audio.play().catch(err => console.error(err));
};
playAudio('/Users/amatthews/Library/CloudStorage/Dropbox/github/ntd-scanning/tauri/src/assets/audio/success.mp3');

报错:Failed to load resource: unsupported URL

当前文件结构

.
├── index.html
├── node_modules
├── src
│	└── assets
│		└── audio
│			├── failure.mp3
│			└── success.mp3
└── src-tauri
	├── tauri.conf.json
	├── icons
	├── src
	└── target

当前tauri.conf.json配置

{
  "$schema": "https://schema.tauri.app/config/1",
  "build": {
    "beforeDevCommand": "npm run dev",
    "beforeBuildCommand": "npm run build",
    "devPath": "http://localhost:1420",
    "distDir": "../dist",
    "withGlobalTauri": true
  },
  "package": {
    "productName": "ntd-scanner",
    "version": "0.1.0"
  },
  "tauri": {
    "allowlist": {
      "all": false,
      "shell": {
        "all": false,
        "open": true
      }
    },
    "windows": [
      {
        "title": "ntd-scanner",
        "width": 800,
        "height": 600
      }
    ],
    "security": {
      "csp": null
    },
    "bundle": {
      "active": true,
      "targets": "all",
      "identifier": "com.ntd-scanner.app",
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ]
    }
  }
}

解决方案

1. 音频文件存放位置

放在src/assets/audio目录下是正确的。这个目录会被前端构建工具(如Vite)自动打包到dist文件夹,最终被包含在Tauri应用包内。无需放到src-tauri目录——除非你需要通过Rust侧读取文件,前端直接播放的话,src下的资源目录适配性更好。

2. 正确引用音频文件的方式

不需要用绝对路径或convertFileSrc(该方法用于访问Rust侧暴露的文件系统路径),直接通过前端构建工具的资源引用机制处理即可,推荐两种方式:

方式一:通过import引入(最稳妥)

让构建工具自动处理资源路径,避免开发/生产环境的路径差异:

// 直接导入音频文件
import successAudio from './assets/audio/success.mp3';
import failureAudio from './assets/audio/failure.mp3';

const playAudio = (audioSrc: string) => {
  const audio = new Audio(audioSrc);
  audio.play().catch(error => {
    console.error('播放失败:', error);
  });
};

// API响应成功时调用
playAudio(successAudio);
// API响应失败时调用
playAudio(failureAudio);

方式二:使用相对路径

如果不想用import,可以使用相对于index.html的路径(注意不要加开头的斜杠):

const playAudio = (path: string) => {
  const audio = new Audio(path);
  audio.play().catch(error => {
    console.error('播放失败:', error);
  });
};

playAudio('assets/audio/success.mp3');

3. 所需的配置修改

(1)确保构建工具复制音频文件

若使用Vite(Tauri默认搭配),会自动处理src/assets下的资源,无需额外配置。如果是其他构建工具,需确保音频文件被复制到dist目录对应位置。

(2)调整内容安全策略(CSP)

虽然当前csp设为null,但生产环境建议配置合理的CSP以允许媒体资源加载,修改tauri.conf.json中的security.csp:

"security": {
  "csp": "default-src 'self'; media-src 'self' blob: data:;"
}

该配置允许从自身域名、Blob和Data URI加载媒体资源,满足音频播放需求。

(3)无需额外权限

音频播放属于前端浏览器API,不需要在Tauri的allowlist中添加额外权限,当前配置已足够。

4. API响应触发播放的问题

浏览器有自动播放策略限制,默认拦截非用户交互触发的音频播放,直接在API回调中播放大概率失败,提供两种解决方法:

方法一:提前获取播放权限

在应用启动后,引导用户进行一次点击操作(比如“确认开始使用”按钮),在点击事件中预加载并静音播放一次音频,获取播放权限:

import successAudio from './assets/audio/success.mp3';

// 监听首次页面点击,仅执行一次
window.addEventListener('click', async () => {
  // 静音播放一次,获取浏览器播放权限
  const audio = new Audio(successAudio);
  audio.muted = true;
  await audio.play().catch(() => {});
  audio.pause();
  // 移除监听
  window.removeEventListener('click', arguments.callee);
}, { once: true });

之后在API响应中播放音频就不会被拦截了。

方法二:通过Rust侧播放音频

绕开浏览器限制,使用Rust的音频库(如rodio)实现播放:

  1. 在src-tauri/Cargo.toml中添加依赖:
[dependencies]
rodio = "0.17"
  1. 在src-tauri/src/main.rs中添加命令:
use rodio::{Decoder, OutputStream, Sink};
use std::fs::File;
use std::path::Path;

#[tauri::command]
fn play_success_audio() {
    // 构建音频文件路径
    let path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../src/assets/audio/success.mp3");
    let file = File::open(path).unwrap();
    let decoder = Decoder::new(file).unwrap();

    // 播放音频
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let sink = Sink::try_new(&stream_handle).unwrap();
    sink.append(decoder);
    sink.sleep_until_end();
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![play_success_audio])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
  1. 在tauri.conf.json的allowlist中添加命令权限:
"allowlist": {
  "all": false,
  "shell": {
    "all": false,
    "open": true
  },
  "commands": {
    "all": false,
    "play_success_audio": true
  }
}
  1. 前端调用该命令:
import { invoke } from '@tauri-apps/api/tauri';

// API响应成功时调用
invoke('play_success_audio').catch(err => console.error(err));

内容的提问来源于stack exchange,提问作者commadelimited

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 07:47:27