33 KiB
wantsong: 我想为Obsidian开发一个插件,这个用什么语言,要做什么环境准备。
Workspace: Sure, here's a proposed directory structure for a Obsidian plugin project
创建工作区...
wantsong: 我想在Obsidian中,当用户粘贴图片时,自动将图片上传至我的S3服务器上,而不是用本地文件夹。在这个工作环境里应该增加哪些文件,代码如何写。
Workspace: Sure, here's a proposed directory structure for a
创建工作区...
wantsong: @terminal 我执行“npm install aws-sdk”时报错,无法将“node.exe”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确, 然后再试一次。
Terminal: 看起来你的 Node.js 环境变量配置有问题。请尝试以下命令来修复:
$env:PATH += ";C:\Tools\Nodejs"
然后重新运行:
npm install aws-sdk
如果问题仍然存在,请检查 Node.js 是否正确安装:
where.exe node
wantsong: 这个服务器是我自行搭建的与S3兼容的服务器,所以我需要指定自己的服务器地址(Endpoint),代码中好像没有这个位置
GitHub Copilot: 你说得对,需要添加自定义的 endpoint 配置。请更新代码如下:
import AWS from 'aws-sdk';
export class S3Uploader {
private s3: AWS.S3;
private bucketName: string;
constructor(bucketName: string, accessKeyId: string, secretAccessKey: string, endpoint?: string, region?: string) {
this.bucketName = bucketName;
this.s3 = new AWS.S3({
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
endpoint: endpoint || 'https://s3.amazonaws.com', // 自定义端点
region: region || 'us-east-1',
s3ForcePathStyle: true, // 对于自建S3兼容服务很重要
signatureVersion: 'v4'
});
}
public async uploadImage(file: File): Promise<string> {
const params = {
Bucket: this.bucketName,
Key: file.name,
Body: file,
ContentType: file.type,
ACL: 'public-read' // Change as needed
};
try {
const data = await this.s3.upload(params).promise();
return data.Location; // Return the URL of the uploaded image
} catch (error) {
console.error('Error uploading image to S3:', error);
throw new Error('Upload failed');
}
}
}
主要改动:
- 添加了
endpoint和region参数 - 设置
s3ForcePathStyle: true,这对自建S3兼容服务很重要 - 添加了
signatureVersion: 'v4'确保兼容性
wantsong: 代码里我修改了实际的数据,请帮我检查下,看是否修改的正确
GitHub Copilot: 检查你的代码,有几个需要注意的地方:
import AWS from 'aws-sdk';
export class S3Uploader {
private s3: AWS.S3;
private bucketName: string;
constructor(bucketName: string, accessKeyId: string, secretAccessKey: string, endpoint?: string, region?: string) {
// 建议使用传入的参数而不是硬编码
this.bucketName = bucketName || 'attachments';
this.s3 = new AWS.S3({
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
endpoint: endpoint || 'https://s3.wantsong.life',
region: region || 'us-east-1',
s3ForcePathStyle: true,
signatureVersion: 'v4'
});
}
public async uploadImage(file: File): Promise<string> {
// 建议添加时间戳避免文件名冲突
const timestamp = Date.now();
const fileName = `${timestamp}-${file.name}`;
const params = {
Bucket: this.bucketName,
Key: fileName,
Body: file,
ContentType: file.type,
ACL: 'public-read'
};
try {
const data = await this.s3.upload(params).promise();
return data.Location;
} catch (error) {
console.error('Error uploading image to S3:', error);
throw new Error('Upload failed');
}
}
}
主要建议:
- 避免在代码中硬编码凭据,建议从配置文件或环境变量读取
- 使用传入的
bucketName参数 - 添加时间戳到文件名避免重复文件覆盖
wantsong: accessKeyId和secretAccessKey,我在哪里指定呢
GitHub Copilot: 你应该通过插件的设置界面来配置这些敏感信息,而不是硬编码在代码中。需要创建以下文件:
export interface S3PluginSettings {
accessKeyId: string;
secretAccessKey: string;
bucketName: string;
endpoint: string;
region: string;
}
export const DEFAULT_SETTINGS: S3PluginSettings = {
accessKeyId: '',
secretAccessKey: '',
bucketName: 'attachments',
endpoint: 'https://s3.amazonaws.com',
region: 'us-east-1'
}
import { App, PluginSettingTab, Setting } from 'obsidian';
import S3UploadPlugin from '../main';
export class S3SettingTab extends PluginSettingTab {
plugin: S3UploadPlugin;
constructor(app: App, plugin: S3UploadPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'S3 Upload Settings' });
new Setting(containerEl)
.setName('Access Key ID')
.setDesc('Your S3 Access Key ID')
.addText(text => text
.setPlaceholder('Enter your access key ID')
.setValue(this.plugin.settings.accessKeyId)
.onChange(async (value) => {
this.plugin.settings.accessKeyId = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Secret Access Key')
.setDesc('Your S3 Secret Access Key')
.addText(text => text
.setPlaceholder('Enter your secret access key')
.setValue(this.plugin.settings.secretAccessKey)
.onChange(async (value) => {
this.plugin.settings.secretAccessKey = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Bucket Name')
.setDesc('S3 bucket name for storing images')
.addText(text => text
.setPlaceholder('Enter bucket name')
.setValue(this.plugin.settings.bucketName)
.onChange(async (value) => {
this.plugin.settings.bucketName = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Endpoint')
.setDesc('S3 endpoint URL')
.addText(text => text
.setPlaceholder('https://s3.amazonaws.com')
.setValue(this.plugin.settings.endpoint)
.onChange(async (value) => {
this.plugin.settings.endpoint = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Region')
.setDesc('S3 region')
.addText(text => text
.setPlaceholder('us-east-1')
.setValue(this.plugin.settings.region)
.onChange(async (value) => {
this.plugin.settings.region = value;
await this.plugin.saveSettings();
}));
}
}
然后修改 s3Uploader.ts,移除硬编码的凭据:
// filepath: d:\AI\obsidian-s3-upload-plugin\src\s3Uploader.ts
import AWS from 'aws-sdk';
export class S3Uploader {
private s3: AWS.S3;
private bucketName: string;
constructor(bucketName: string, accessKeyId: string, secretAccessKey: string, endpoint?: string, region?: string) {
this.bucketName = bucketName;
this.s3 = new AWS.S3({
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
endpoint: endpoint,
region: region,
s3ForcePathStyle: true,
signatureVersion: 'v4'
});
}
// ...existing code...
}
这样用户就可以在插件设置中安全地配置自己的 S3 凭据了。
找到具有 2 个许可证类型的类似代码
wantsong: @workspace 你能否看到我所有的工程,看应该改文件还是增加文件,每个文件应该怎么修改
Workspace: 正在收集工作区信息我已经仔细查看了你的工程结构。你的代码有一些不一致的地方需要修复。以下是需要修改的文件:
1. 修复 settings.ts
import { Plugin } from 'obsidian';
export interface S3UploadSettings {
awsAccessKeyId: string;
awsSecretAccessKey: string;
bucketName: string;
region: string;
endpoint?: string;
}
const DEFAULT_SETTINGS: S3UploadSettings = {
awsAccessKeyId: '',
awsSecretAccessKey: '',
bucketName: '',
region: 'us-east-1',
endpoint: ''
};
export default class Settings {
private plugin: Plugin;
public awsAccessKeyId: string;
public awsSecretAccessKey: string;
public bucketName: string;
public region: string;
public endpoint?: string;
constructor(plugin: Plugin) {
this.plugin = plugin;
this.awsAccessKeyId = DEFAULT_SETTINGS.awsAccessKeyId;
this.awsSecretAccessKey = DEFAULT_SETTINGS.awsSecretAccessKey;
this.bucketName = DEFAULT_SETTINGS.bucketName;
this.region = DEFAULT_SETTINGS.region;
this.endpoint = DEFAULT_SETTINGS.endpoint;
}
async load(): Promise<void> {
const data = await this.plugin.loadData();
if (data) {
this.awsAccessKeyId = data.awsAccessKeyId || DEFAULT_SETTINGS.awsAccessKeyId;
this.awsSecretAccessKey = data.awsSecretAccessKey || DEFAULT_SETTINGS.awsSecretAccessKey;
this.bucketName = data.bucketName || DEFAULT_SETTINGS.bucketName;
this.region = data.region || DEFAULT_SETTINGS.region;
this.endpoint = data.endpoint || DEFAULT_SETTINGS.endpoint;
}
}
async save(): Promise<void> {
await this.plugin.saveData({
awsAccessKeyId: this.awsAccessKeyId,
awsSecretAccessKey: this.awsSecretAccessKey,
bucketName: this.bucketName,
region: this.region,
endpoint: this.endpoint
});
}
}
2. 修复 s3Uploader.ts
import AWS from 'aws-sdk';
import Settings from './settings';
export default class S3Uploader {
private s3: AWS.S3;
private settings: Settings;
constructor(settings: Settings) {
this.settings = settings;
this.initializeS3();
}
private initializeS3(): void {
const config: AWS.S3.ClientConfiguration = {
accessKeyId: this.settings.awsAccessKeyId,
secretAccessKey: this.settings.awsSecretAccessKey,
region: this.settings.region,
signatureVersion: 'v4'
};
if (this.settings.endpoint) {
config.endpoint = this.settings.endpoint;
config.s3ForcePathStyle = true;
}
this.s3 = new AWS.S3(config);
}
public async uploadImage(file: File): Promise<string> {
if (!this.settings.bucketName || !this.settings.awsAccessKeyId || !this.settings.awsSecretAccessKey) {
throw new Error('S3 credentials not configured');
}
// 重新初始化S3客户端以确保使用最新的设置
this.initializeS3();
const fileName = `${Date.now()}-${file.name}`;
const params: AWS.S3.PutObjectRequest = {
Bucket: this.settings.bucketName,
Key: fileName,
Body: file,
ContentType: file.type,
ACL: 'public-read'
};
try {
const data = await this.s3.upload(params).promise();
return data.Location;
} catch (error) {
console.error('Error uploading image to S3:', error);
throw new Error(`Upload failed: ${error.message}`);
}
}
}
3. 修复 main.ts
import { Plugin, Notice, ClipboardManager } from 'obsidian';
import S3Uploader from './s3Uploader';
import Settings from './settings';
import SettingTab from './ui/settingTab';
export default class S3UploadPlugin extends Plugin {
private s3Uploader: S3Uploader;
private settings: Settings;
async onload() {
console.log('Loading S3 Upload Plugin');
this.settings = new Settings(this);
await this.settings.load();
this.s3Uploader = new S3Uploader(this.settings);
this.addSettingTab(new SettingTab(this.app, this.s3Uploader, this.settings));
// 注册粘贴事件处理器
this.registerDomEvent(document, 'paste', this.handlePaste.bind(this));
}
private async handlePaste(event: ClipboardEvent): Promise<void> {
const clipboardData = event.clipboardData;
if (!clipboardData) return;
const items = Array.from(clipboardData.items);
const imageItem = items.find(item => item.type.startsWith('image/'));
if (imageItem) {
event.preventDefault();
const file = imageItem.getAsFile();
if (file) {
try {
new Notice('正在上传图片到S3...');
const imageUrl = await this.s3Uploader.uploadImage(file);
// 获取当前活动的编辑器
const activeView = this.app.workspace.getActiveViewOfType(require('obsidian').MarkdownView);
if (activeView) {
const editor = activeView.editor;
const cursor = editor.getCursor();
const markdownLink = ``;
editor.replaceRange(markdownLink, cursor);
}
new Notice('图片上传成功!');
} catch (error) {
console.error('Upload error:', error);
new Notice(`图片上传失败: ${error.message}`);
}
}
}
}
onunload() {
console.log('Unloading S3 Upload Plugin');
}
}
4. 修复 settingTab.ts
import { App, PluginSettingTab, Setting } from 'obsidian';
import S3Uploader from '../s3Uploader';
import Settings from '../settings';
export default class SettingTab extends PluginSettingTab {
private uploader: S3Uploader;
private settings: Settings;
constructor(app: App, uploader: S3Uploader, settings: Settings) {
super(app, app.plugins.getPlugin('obsidian-s3-upload-plugin'));
this.uploader = uploader;
this.settings = settings;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'S3上传插件设置' });
new Setting(containerEl)
.setName('AWS Access Key ID')
.setDesc('输入你的AWS Access Key ID')
.addText(text => text
.setPlaceholder('输入Access Key ID')
.setValue(this.settings.awsAccessKeyId)
.onChange(async (value) => {
this.settings.awsAccessKeyId = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('AWS Secret Access Key')
.setDesc('输入你的AWS Secret Access Key')
.addText(text => {
text.inputEl.type = 'password';
return text
.setPlaceholder('输入Secret Access Key')
.setValue(this.settings.awsSecretAccessKey)
.onChange(async (value) => {
this.settings.awsSecretAccessKey = value;
await this.settings.save();
});
});
new Setting(containerEl)
.setName('S3存储桶名称')
.setDesc('输入你的S3存储桶名称')
.addText(text => text
.setPlaceholder('输入存储桶名称')
.setValue(this.settings.bucketName)
.onChange(async (value) => {
this.settings.bucketName = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('区域')
.setDesc('输入你的S3存储桶区域')
.addText(text => text
.setPlaceholder('如: us-east-1')
.setValue(this.settings.region)
.onChange(async (value) => {
this.settings.region = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('自定义端点 (可选)')
.setDesc('如果使用自建S3兼容服务,请输入端点URL')
.addText(text => text
.setPlaceholder('如: https://s3.example.com')
.setValue(this.settings.endpoint || '')
.onChange(async (value) => {
this.settings.endpoint = value;
await this.settings.save();
}));
}
}
5. 修复 manifest.json
{
"id": "obsidian-s3-upload-plugin",
"name": "S3图片上传插件",
"version": "1.0.0",
"description": "自动将粘贴的图片上传到S3存储服务",
"author": "Your Name",
"minAppVersion": "0.15.0",
"main": "main.js"
}
主要修复内容:
- 统一了设置接口和类:所有文件现在都使用相同的
Settings类 - 实现了正确的Obsidian设置存储:使用
loadData()和saveData()方法 - 修复了导入导出问题:确保所有的导入都使用
default导出 - 改进了粘贴事件处理:使用更可靠的DOM事件监听
- 添加了错误处理和用户反馈:显示上传状态通知
- 支持自定义S3端点:允许使用自建的S3兼容服务
现在你的插件应该能够正常工作了。记得运行 npm run build 来编译插件。
wantsong: @workspace 第一句话报错:模块“"obsidian"”没有导出的成员“ClipboardManager”。在整个工程里,我应该安装哪些依赖包
Workspace: 正在收集工作区信息我看到你的项目中有几个问题需要修复。首先,ClipboardManager 不是 Obsidian API 的导出成员,而且你需要安装正确的依赖包。
1. 修复 main.ts 的导入问题
import { Plugin, Notice, MarkdownView } from 'obsidian';
import S3Uploader from './s3Uploader';
import Settings from './settings';
import SettingTab from './ui/settingTab';
export default class S3UploadPlugin extends Plugin {
private s3Uploader: S3Uploader;
private settings: Settings;
async onload() {
console.log('Loading S3 Upload Plugin');
this.settings = new Settings(this);
await this.settings.load();
this.s3Uploader = new S3Uploader(this.settings);
this.addSettingTab(new SettingTab(this.app, this, this.s3Uploader, this.settings));
// 注册粘贴事件处理器
this.registerDomEvent(document, 'paste', this.handlePaste.bind(this));
}
private async handlePaste(event: ClipboardEvent): Promise<void> {
const clipboardData = event.clipboardData;
if (!clipboardData) return;
const items = Array.from(clipboardData.items);
const imageItem = items.find(item => item.type.startsWith('image/'));
if (imageItem) {
event.preventDefault();
const file = imageItem.getAsFile();
if (file) {
try {
new Notice('正在上传图片到S3...');
const imageUrl = await this.s3Uploader.uploadImage(file);
// 获取当前活动的编辑器
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView) {
const editor = activeView.editor;
const cursor = editor.getCursor();
const markdownLink = ``;
editor.replaceRange(markdownLink, cursor);
}
new Notice('图片上传成功!');
} catch (error) {
console.error('Upload error:', error);
new Notice(`图片上传失败: ${error.message}`);
}
}
}
}
onunload() {
console.log('Unloading S3 Upload Plugin');
}
}
2. 修复 settingTab.ts 的构造函数
import { App, PluginSettingTab, Setting } from 'obsidian';
import S3Uploader from '../s3Uploader';
import Settings from '../settings';
import S3UploadPlugin from '../main';
export default class SettingTab extends PluginSettingTab {
private uploader: S3Uploader;
private settings: Settings;
constructor(app: App, plugin: S3UploadPlugin, uploader: S3Uploader, settings: Settings) {
super(app, plugin);
this.uploader = uploader;
this.settings = settings;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'S3上传插件设置' });
new Setting(containerEl)
.setName('AWS Access Key ID')
.setDesc('输入你的AWS Access Key ID')
.addText(text => text
.setPlaceholder('输入Access Key ID')
.setValue(this.settings.awsAccessKeyId)
.onChange(async (value) => {
this.settings.awsAccessKeyId = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('AWS Secret Access Key')
.setDesc('输入你的AWS Secret Access Key')
.addText(text => {
text.inputEl.type = 'password';
return text
.setPlaceholder('输入Secret Access Key')
.setValue(this.settings.awsSecretAccessKey)
.onChange(async (value) => {
this.settings.awsSecretAccessKey = value;
await this.settings.save();
});
});
new Setting(containerEl)
.setName('S3存储桶名称')
.setDesc('输入你的S3存储桶名称')
.addText(text => text
.setPlaceholder('输入存储桶名称')
.setValue(this.settings.bucketName)
.onChange(async (value) => {
this.settings.bucketName = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('区域')
.setDesc('输入你的S3存储桶区域')
.addText(text => text
.setPlaceholder('如: us-east-1')
.setValue(this.settings.region)
.onChange(async (value) => {
this.settings.region = value;
await this.settings.save();
}));
new Setting(containerEl)
.setName('自定义端点 (可选)')
.setDesc('如果使用自建S3兼容服务,请输入端点URL')
.addText(text => text
.setPlaceholder('如: https://s3.example.com')
.setValue(this.settings.endpoint || '')
.onChange(async (value) => {
this.settings.endpoint = value;
await this.settings.save();
}));
}
}
3. 更新 package.json 依赖包
{
"name": "obsidian-s3-upload-plugin",
"version": "1.0.0",
"description": "An Obsidian plugin that uploads images to an S3 server when pasted.",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "Wantsong",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"aws-sdk": "^2.1692.0"
}
}
4. 更新 esbuild.config.mjs
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["src/main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}
5. 需要安装的依赖包
运行以下命令来安装所有必要的依赖包:
npm install --save aws-sdk
npm install --save-dev @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser builtin-modules esbuild obsidian tslib typescript
主要修复内容:
- 移除了不存在的
ClipboardManager导入 - 正确导入了
MarkdownView - 修复了
SettingTab构造函数参数 - 更新了 package.json 依赖配置
- 改进了 esbuild.config.mjs 配置
现在你可以运行 npm install 然后 npm run build 来编译你的插件了。
找到具有 2 个许可证类型的类似代码
wantsong: @terminal 你能看见终端上的报错么,报了四个锁雾
Terminal:
wantsong: @terminal 你能看见终端上的报错么,执行“npm run build”时,报了四个错误
Terminal: 是的,我看到了 TypeScript 编译错误。需要修复这些类型问题:
code src/main.ts
在 main.ts 中修复以下问题:
-
在第7行和第8行的属性后添加
!断言符:private s3Uploader!: S3Uploader; private settings!: Settings; -
在第51行将
error转换为Error类型:new Notice(`图片上传失败: ${(error as Error).message}`);
然后修复 s3Uploader.ts:
code src/s3Uploader.ts
在第52行将 error 转换为 Error 类型:
throw new Error(`Upload failed: ${(error as Error).message}`);
修复完成后重新构建:
npm run build
wantsong: @terminal build应该是完成了,这个插件build完成后在哪里?我怎么在Obsidian中用它
Terminal: 构建完成后,插件文件在当前目录下。你需要将插件复制到 Obsidian 的插件目录:
$obsidianPluginsPath = "$env:APPDATA\Obsidian\plugins\obsidian-s3-upload-plugin"
New-Item -ItemType Directory -Path $obsidianPluginsPath -Force
Copy-Item -Path ".\main.js", ".\manifest.json", ".\styles.css" -Destination $obsidianPluginsPath
然后在 Obsidian 中:
- 打开设置 (Ctrl+,)
- 转到"社区插件"
- 关闭安全模式(如果已开启)
- 在已安装插件列表中找到"S3 Upload Plugin"并启用它
如果没有 styles.css 文件,只复制前两个文件:
Copy-Item -Path ".\main.js", ".\manifest.json" -Destination $obsidianPluginsPath
wantsong: 当我在 Obsidian 中启用这个插件时,提示我启用失败。我在哪里可以看到错误原因
GitHub Copilot: 你可以通过以下几种方式查看错误原因:
1. 查看 Obsidian 开发者控制台
按 Ctrl+Shift+I 打开开发者工具,然后查看 Console 标签页中的错误信息。
2. 检查插件文件是否完整
确认这些文件都存在于插件目录中:
$pluginPath = "$env:APPDATA\Obsidian\plugins\obsidian-s3-upload-plugin"
Get-ChildItem $pluginPath
应该至少包含:
- main.js
- manifest.json
3. 检查 manifest.json 是否正确
查看 manifest.json 内容:
Get-Content "$env:APPDATA\Obsidian\plugins\obsidian-s3-upload-plugin\manifest.json"
确保内容类似:
{
"id": "obsidian-s3-upload-plugin",
"name": "S3图片上传插件",
"version": "1.0.0",
"description": "自动将粘贴的图片上传到S3存储服务",
"author": "Your Name",
"minAppVersion": "0.15.0",
"main": "main.js"
}
4. 常见问题排查
- 检查 main.js 文件大小:如果文件很小(几KB),说明编译可能有问题
- 重新编译插件:
npm run build - 重新复制文件:
Copy-Item -Path ".\main.js", ".\manifest.json" -Destination "$env:APPDATA\Obsidian\plugins\obsidian-s3-upload-plugin" -Force
请先打开开发者控制台查看具体的错误信息,然后告诉我看到了什么错误。
wantsong: 我把错误日志下载了下来,你可以看见。这个怎么解决
GitHub Copilot: 这个错误是因为 aws-sdk 在 Electron 环境中需要 Node.js 的 buffer 模块,但 Obsidian 插件环境中没有这个模块。我们需要改用浏览器兼容的 S3 客户端。
首先卸载 aws-sdk 并安装 @aws-sdk/client-s3:
npm uninstall aws-sdk
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage
然后更新 src/s3Uploader.ts:
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { Upload } from '@aws-sdk/lib-storage';
import Settings from './settings';
export default class S3Uploader {
private s3Client: S3Client;
private settings: Settings;
constructor(settings: Settings) {
this.settings = settings;
this.initializeS3();
}
private initializeS3(): void {
const config: any = {
region: this.settings.region,
credentials: {
accessKeyId: this.settings.awsAccessKeyId,
secretAccessKey: this.settings.awsSecretAccessKey,
},
};
if (this.settings.endpoint) {
config.endpoint = this.settings.endpoint;
config.forcePathStyle = true;
}
this.s3Client = new S3Client(config);
}
public async uploadImage(file: File): Promise<string> {
if (!this.settings.bucketName || !this.settings.awsAccessKeyId || !this.settings.awsSecretAccessKey) {
throw new Error('S3 credentials not configured');
}
// 重新初始化S3客户端以确保使用最新的设置
this.initializeS3();
const fileName = `${Date.now()}-${file.name}`;
try {
const upload = new Upload({
client: this.s3Client,
params: {
Bucket: this.settings.bucketName,
Key: fileName,
Body: file,
ContentType: file.type,
ACL: 'public-read'
}
});
const result = await upload.done();
// 构建文件 URL
const baseUrl = this.settings.endpoint || `https://s3.${this.settings.region}.amazonaws.com`;
return `${baseUrl}/${this.settings.bucketName}/${fileName}`;
} catch (error) {
console.error('Error uploading image to S3:', error);
throw new Error(`Upload failed: ${(error as Error).message}`);
}
}
}
更新 package.json 的依赖:
{
"name": "obsidian-s3-upload-plugin",
"version": "1.0.0",
"description": "An Obsidian plugin that uploads images to an S3 server when pasted.",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "Wantsong",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.0.0",
"@aws-sdk/lib-storage": "^3.0.0"
}
}
然后重新构建:
npm install
npm run build
重新复制插件文件:
Copy-Item -Path ".\main.js", ".\manifest.json" -Destination "$env:APPDATA\Obsidian\plugins\obsidian-s3-upload-plugin" -Force
新的 AWS SDK v3 是专门为现代浏览器和 Electron 环境设计的,应该能解决 buffer 模块的问题。
找到具有 1 个许可证类型的类似代码