From b331252bfb9949230dc8fb97f8479c6f4c73e562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=8C=E8=90=8C=E5=93=92=E8=B5=AB=E8=90=9D?= Date: Sun, 17 Sep 2023 02:03:15 -0700 Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=83:=20V2.2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 47 +- Version_update_log.md | 816 ++++++++-------- .../plugins/GeneratedPluginRegistrant.java | 59 +- assets/files/UpdateLog.md | 818 ++++++++-------- ios/Flutter/Generated.xcconfig | 2 +- ios/Flutter/flutter_export_environment.sh | 2 +- ios/Runner/GeneratedPluginRegistrant.m | 37 +- lib/album/album_page.dart | 14 +- lib/api/alist_api.dart | 256 ++--- lib/api/aliyun_api.dart | 204 ++-- lib/api/aws_api.dart | 30 +- lib/api/ftp_api.dart | 168 ++-- lib/api/github_api.dart | 178 ++-- lib/api/imgur_api.dart | 135 ++- lib/api/lskypro_api.dart | 121 +-- lib/api/qiniu_api.dart | 166 ++-- lib/api/smms_api.dart | 93 +- lib/api/tencent_api.dart | 269 +++--- lib/api/upyun_api.dart | 262 +++-- lib/api/webdav_api.dart | 99 +- .../rename_uploaded_file.dart | 2 +- .../select_default_picture_host.dart | 709 ++++---------- lib/pages/home_page.dart | 134 ++- lib/pages/pichoro_app.dart | 4 +- lib/pages/upload_pages/upload_status.dart | 2 +- lib/pages/upload_pages/upload_utils.dart | 914 ++++-------------- .../configure_page/alist_configure.dart | 53 +- .../configure_page/aliyun_configure.dart | 133 +-- .../configure_page/aws_configure.dart | 63 +- .../configure_page/ftp_configure.dart | 122 +-- .../configure_page/github_configure.dart | 167 ++-- .../configure_page/imgur_configure.dart | 114 +-- .../configure_page/lskypro_configure.dart | 50 +- .../configure_page/qiniu_configure.dart | 102 +- .../configure_page/smms_configure.dart | 55 +- .../configure_page/tencent_configure.dart | 167 +--- .../configure_page/upyun_configure.dart | 272 ++---- .../configure_page/webdav_configure.dart | 70 +- .../upyun_configure_store_edit.dart | 54 +- .../webdav_configure_store_edit.dart | 1 - .../configure_store/configure_store_page.dart | 5 +- .../default_picture_host_select.dart | 196 +--- .../alist/alist_download_manage_page.dart | 2 +- .../download_api/alist_download_task.dart | 2 +- .../alist/download_api/alist_downloader.dart | 36 +- .../alist/upload_api/alist_upload_utils.dart | 5 - .../download_api/aliyun_downloader.dart | 6 - .../upload_api/aliyun_upload_utils.dart | 5 - .../aws/download_api/aws_downloader.dart | 34 +- .../aws/upload_api/aws_upload_utils.dart | 5 - .../base_download_manage_page.dart | 7 +- .../file_explorer/local_image_preview.dart | 2 +- .../picture_host_manage_entry.dart | 2 +- .../ftp/download_api/sftp_downloader.dart | 6 - .../ftp/upload_api/sftp_upload_utils.dart | 5 - .../download_api/github_downloader.dart | 6 - .../upload_api/github_upload_utils.dart | 5 - .../imgur/download_api/imgur_downloader.dart | 6 - .../imgur/imgur_file_explorer.dart | 4 +- .../imgur/upload_api/imgur_upload_utils.dart | 5 - .../download_api/lskypro_downloader.dart | 6 - .../upload_api/lskypro_upload_utils.dart | 5 - .../manage_api/alist_manage_api.dart | 678 +++++-------- .../manage_api/aliyun_manage_api.dart | 786 +++++++-------- .../manage_api/aws_manage_api.dart | 491 +++++----- .../manage_api/ftp_manage_api.dart | 117 ++- .../manage_api/github_manage_api.dart | 481 ++++----- .../manage_api/imgur_manage_api.dart | 5 +- .../manage_api/lskypro_manage_api.dart | 166 ++-- .../manage_api/qiniu_manage_api.dart | 577 +++++------ .../manage_api/smms_manage_api.dart | 175 ++-- .../manage_api/tencent_manage_api.dart | 753 ++++++--------- .../manage_api/upyun_manage_api.dart | 382 ++------ .../manage_api/webdav_manage_api.dart | 100 +- .../qiniu/download_api/qiniu_downloader.dart | 6 - .../qiniu/qiniu_bucket_list_page.dart | 2 +- .../qiniu/upload_api/qiniu_upload_utils.dart | 5 - .../smms/download_api/smms_downloader.dart | 6 - .../smms/upload_api/smms_upload_utils.dart | 5 - .../download_api/tencent_downloader.dart | 34 +- .../upload_api/tencent_upload_utils.dart | 5 - .../upyun/download_api/upyun_downloader.dart | 7 - .../upyun/upload_api/upyun_upload_utils.dart | 5 - .../upyun/upyun_bucket_list_page.dart | 31 + .../upyun/upyun_login.dart | 3 +- .../download_api/webdav_downloader.dart | 6 - .../upload_api/webdav_upload_utils.dart | 5 - lib/utils/common_functions.dart | 49 +- lib/utils/picbed/upyun.dart | 46 + lib/utils/uploader.dart | 30 +- pubspec.lock | 8 + pubspec.yaml | 3 +- 92 files changed, 4778 insertions(+), 7508 deletions(-) create mode 100644 lib/utils/picbed/upyun.dart diff --git a/README.md b/README.md index cb69069..b25bede 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ -  一款基于flutter的手机端云存储平台/图床管理和文件上传/下载工具,最新版本**V2.1.3**,与PicGo配置互通,可直接扫码导入,主要功能包括云存储/图床/云服务器平台,以及网盘管理(通过[Alist](https://alist.nn.ci/zh/)),文件上传和下载管理,以及各种格式的链接分享。 +  一款基于flutter的手机端云存储平台/图床管理和文件上传/下载工具,最新版本**V2.2.2**,与PicGo配置互通,可直接扫码导入,主要功能包括云存储/图床/云服务器平台,以及网盘管理(通过[Alist](https://alist.nn.ci/zh/)),文件上传和下载管理,以及各种格式的链接分享。   支持多种图片/PDF/文本文件/音视频的在线预览和播放,具体支持的格式请查看[支持的格式列表](https://github.com/Kuingsmile/PicHoro/blob/main/supported_format.md "支持的格式列表") @@ -72,7 +72,7 @@ Github下载地址 [Github release](https://github.com/Kuingsmile/PicHoro/releases) -我的个人网站提供的最新版本下载地址 [https://pichoro.msq.pub/PicHoro_V2.1.3.apk](https://pichoro.msq.pub/PicHoro_V2.1.3.apk) +我的个人网站提供的最新版本下载地址 [https://pichoro.msq.pub/PicHoro_V2.2.2.apk](https://pichoro.msq.pub/PicHoro_V2.2.2.apk) ### IOS @@ -109,33 +109,22 @@ Github下载地址 [Github release](https://github.com/Kuingsmile/PicHoro/releas   详细更新日志请查看[更新日志](https://github.com/Kuingsmile/PicHoro/blob/main/Version_update_log.md "更新日志") -- 2023-08-26: **V2.1.3**: - - - 依赖:从Flutter 3.3版本迁移至 3.13 版本 - - 优化:移除了底部栏的文字标签 - - 更新:阿里云存储桶区域现在支持无地域属性 - - 更新:腾讯云移除了不再支持的莫斯科区域 - - 更新:FTP现在删除功能会额外校验路径,避免误删除 - -- 2023-07-25: **V2.1.2**: - - - 修复:修复了imgur仓库无法进入的问题 - -- 2023-06-20: **V2.1.1**: - - - 更新:更新了alist驱动列表,与最新版(3.21.0)保持同步 - -- 2023-06-20: **V2.1.0**: - - - webdav现在支持设置自定义域名 - - ftp现在支持设置自定义域名 - -## 开发计划 - -- 添加搜索功能 -- 添加生成限时分享链接功能 -- 优化文件数量过多时的加载速度 -- 修复bug +### 2023-09-17 **V2.2.2** + +- 新增:又拍云现在支持设置防盗链token和过期时间参数 +- 新增:现在首页标题会显示当前的默认上传图床 +- 新增:时间戳重命名现在不再额外添加5个随机字符 +- 新增:自定义重命名中的时间戳选项现在采用毫秒,而不是秒 +- 新增:移除了二维码导入配置时的校验步骤以提高导入速度 +- 新增:现在保存图床设置时不再强制进行校验(与`校验当前配置`功能重复) +- 新增:现在拷贝链接时,会对文件名进行basename处理 +- 新增:现在查看配置时密码不再加密显示,方便配置 +- 修复:修复了通过拍照上传时,文件名中含有的二级路径没有生效的问题 +- 修复:修复了文件名中含有二级路径时,开启图片压缩后无法上传的问题 +- 修复:imgur移除了失效的CDN加速网址 +- 修复:修复了文件名中含有二级路径时,部分图床无法远程删除图片的问题 +- 修复:修复了从二维码导入时,路径设置为根目录时导入错误的问题 +- 维护:重构了大部分代码以提高性能和可维护性 ## 开发交流 diff --git a/Version_update_log.md b/Version_update_log.md index 5b47e36..5c9cc0f 100644 --- a/Version_update_log.md +++ b/Version_update_log.md @@ -1,216 +1,216 @@ # 更新日志 -## 文件预览支持 - -| 平台 | 图片 | PDF | 文本文件 | 视频 | -| ------ | :---: | :---: | :------: | :---: | -| Alist | ✅ | ✅ | ✅ | ✅ | -| 阿里云 | ✅ | ✅ | ✅ | ✅ | -| S3 | ✅ | ✅ | ✅ | ✅ | -| 腾讯云 | ✅ | ✅ | ✅ | ✅ | -| 又拍云 | ✅ | ✅ | ✅ | ✅ | -| 七牛云 | ✅ | ✅ | ✅ | ✅ | -| WebDAV | ✅ | ✅ | ✅ | ✅ | -| FTP | ✅ | ❌ | ✅ | ❌ | -| Github | ✅ | ❌ | ✅ | ❌ | -| Imgur | ✅ | ❌ | ❌ | ✅ | -| 兰空 | ✅ | ❌ | ❌ | ❌ | -| SM.MS | ✅ | ❌ | ❌ | ❌ | - -- 2023-08-26: **V2.1.3**: - - - 依赖:从Flutter 3.3版本迁移至 3.13 版本 - - 优化:移除了底部栏的文字标签 - - 更新:阿里云存储桶区域现在支持无地域属性 - - 更新:腾讯云移除了不再支持的莫斯科区域 - - 更新:FTP现在删除功能会额外校验路径,避免误删除 - -- 2023-07-25: **V2.1.2**: - - - 修复:修复了imgur仓库无法进入的问题 - -- 2023-06-20: **V2.1.1**: - - - 更新:更新了alist驱动列表,与最新版(3.21.0)保持同步 - -- 2023-06-20: **V2.1.0**: - - - webdav现在支持设置自定义域名 - - ftp现在支持设置自定义域名 - -- 2023-05-04: **V2.0.0**: - - - 移除了用户登录和云端同步系统,现在所有数据保存于用户本地 - - 移除了imgur管理登录页面对clientsecret的需求 - - 更新了alist驱动列表,与最新版(3.16.3)保持同步 - - 修复了重复设置Alist为默认图床时,默认相册设置错误的问题 - -- 2023-04-14: **V1.11.0**: - - - 维护:更换了github加速代理。 - - 修复:修复了复制链接时如果链接中有逗号会导致格式错误的问题。 - - 修复:修复了alist复制链接格式错误的问题。 - -- 2023-02-28: **V1.10.0**: - - - 新增:s3/阿里云/腾讯云等平台现在可以单独为存储桶设置自定义域名了。 - - 新增:现在会在安装或者启动时获取安装未知应用权限,避免APP无法启动。 - - 维护:部分代码精简 - - 修复:修复了s3平台文件地址错误的问题。 - - 修复:修复了图片缓存导致相同地址的图片无法更新的问题。 - -- 2023-01-05: **V1.99**: - - 新增:s3/阿里云/腾讯云等平台现在可以单独为存储桶设置自定义域名了。 - - 新增:现在会在安装或者启动时获取安装未知应用权限,避免APP无法启动。 - - 维护:部分代码精简 - - 修复:修复了s3平台文件地址错误的问题。 - - 修复:修复了图片缓存导致相同地址的图片无法更新的问题。 - -- 2023-01-05: **V1.99**: - - 新增:更换了版本升级使用的下载地址为cdn加速地址,优化了下载速度。 - - 新增:将作者QQ二维码修改为软件反馈交流群二维码。 - - 维护:精简了部分通用代码,方便后续维护和功能扩展,共减少约8000行冗余代码。 - - 修复:修复了设置主题为自动后,APP无法正常启动的问题。 - - 修复:修复了在上传下载页面删除上传任务后,使用`全部开始`按钮仍然会上传已删除的任务的问题。 - - 修复:修复了在进入webdav上传下载页面时,自动切换标签页没有正常作用的问题。 - -- 2022-12-18: **V1.98**: - - 新增:添加了图片压缩功能,现在可以选择在上传图片前先进行压缩了,可选压缩后格式为jpg、png和webp,并且可以自定义最小宽度、最小高度和压缩后质量。 - - 新增:S3 API兼容平台上传时现在会主动修改content-type。 - - 优化:添加了webp文件格式的图标。 - -- 2022-12-05: **V1.97**: - - 新增:添加了对**WebDAV**的支持,使用坚果云webdav和Alist V3的webdav测试通过。 - - 新增:Alist V3现在可以不登录访问了,只需要设置Alist域名,即可在管理页面中查看文件。 - - 修复:修复了清空相册数据库页面没有显示阿里云的问题。 - -- 2022-12-02: **V1.96**: - - - 新增:新增了对[Alist V3](https://alist.nn.ci/zh/)的支持,现在可以通过Alist间接管理其支持的各种存储和网盘。支持的平台包括:本地存储,阿里云盘,百度网盘,夸克网盘,蓝奏云,谷歌相册,115,OneDrive,天翼云盘,GoogleDrive,123云盘,FTP / SFTP,PikPak,S3,又拍云,WebDav,Teambition,分秒帧,移动云盘等等。 - - Alist的token有有效期,PicHoro会每天尝试更新一次token,如果遇到错误,请尝试重新设置Alist来更新token。 - - 支持通过Alist来查看图片和观看视频。 - - 支持快捷操作Alist存储,包括新增,卸载,修改配置,启用,禁用等。 - - 支持上传/下载,新建文件夹/重命名/删除/分享链接等各种操作,使用百度网盘官方接口时,会自动添加`User-Agent:pan.baidu.com`的请求头。 - - 由于Alist支持的平台比较多,个人一些平台没有账号,未能全部完成测试,如果发现有问题,请提交issue。已经测试过的平台有: - - Alist V3 - - 阿里云盘 - - 百度网盘 - - 本机存储 - - SFTP - - FTP - - 一刻相册 - - WebDav(使用坚果云webdav测试) - - 新增:增加了对**PDF**格式文件在线预览的支持,可以搜索和跳转页面。 - - 新增:增加了对**音视频文件**在线播放的支持,其中: - - mp4, flv,m4v,mp3,avi,mpg,flac,ogg,ts,aac,m4a,vob等格式支持播放列表功能,列表由当前目录下的视频文件组成。 - - mkv和rmvb等格式由于后台实现的问题,暂时不支持播放列表功能。 - - mkv文件可以自动识别和加载字幕文件,字幕文件需要与视频的文件名相同,后缀名为srt/ass/vtt/sbv/ttml/dfxp/ssa之一。 - - **为了解码mkv等格式,添加了VLC依赖,包体积增加较多,我在尝试缩减和考虑是否提供不带VLC的版本。** - - 新增:现在支持**更多的文本文件格式**预览,涵盖了各种常见编程语言源文件。 - - 新增:图床管理页面增加了重置排序按钮,可以将图床的排序重置为默认排序。 - - 优化:压缩了部分assets文件,尽量减少对包体积的影响,同时删除了部分未使用的assets文件。 - - 优化:更换了网页浏览的实现方式。 - - 优化:用户登录后的图床信息查看页,现在按照图床名称排序。 - - 优化:优化部分UI尺寸。 - - 优化:优化了部分冗余代码。 - - 修复:修复了图片预览时会在最后额外添加一页空白页的问题 - - 修复:修复了注销登录时没有清空S3上传/下载列表的问题。 - - 修复:修复了连续上传模式下,使用S3上传时相册显示不正确的问题。 - - 修复:修复了图床设置为S3时,相册内删除会错误的将url当做本地文件名进行删除的问题。 - - 修复:修复了使用S3时,返回的图片URL不包括存储桶,导致图片无法显示的问题。 - - 修复:修复了图床管理页面的排序问题。 - -- 2022-11-25: **V1.95**: - - - 新增:设置界面现在在有新版本时会显示新版本号,且`检查更新`会变为`有新版本`来提示用户。 - - 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 - - 优化:调整了上传/下载界面和主页上传框任务列表的顺序,现在后添加的任务会显示在最上面。 - - 优化:调整了上传界面和主页上传框任务列表的UI,进度条修改到任务名称的右侧,减少单个任务的宽度。 - - 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 - - 优化:修改了部分界面UI。 - - 修复:修复了在上传界面和主页上传框内,点击全部上传时,已上传完成的任务会被重新上传的问题。 - - 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 - - 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 -- 2022-11-24: **V1.94 beta 2**: - - - 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 - - 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 - - 优化:修改了部分界面UI。 - - 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 - -- 2022-11-23: **V1.94 beta 1**: - - - 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 - -- 2022-11-22: **V1.93**: - - - 新增:现在进入联系作者界面时会自动复制作者QQ号到剪贴板。 - - 优化:修改了连续上传模式的逻辑,由`拍照->校验->上传->拍照`改为`拍照->校验(异步上传)->拍照`,将上传操作修改为异步执行,提高用户体验。 - - 优化:修改了部分提示信息。 - - 优化:解决了上传界面和相册界面的右上角设置弹出栏,必须点击文字部分才能弹出窗口的问题。 - - 优化:优化了APP的启动速度。 - - 修复:修复了aws上传和下载列表会在APP关闭后被清空的问题。 - - 修复:修复了批量复制链接时,从第二行开始,开头会多一个空格的问题。 -- 2022-11-21: **V1.92**: - - - 优化:更换了github和七牛云的图标,优化在深色模式下的显示效果 - - 修复:修复了登录时没有同步FTP和S3平台配置的问题 - - 修复:修复了注销登录后会导致图床管理页面无法正常显示的问题 -- 2022-11-19: **V1.91**: - - - 新增:增加了对**兼容S3 API协议的平台**的支持,目前已测试通过的平台包括**AWS S3,backblaze和cloudfare R2**,其他平台只要兼容S3 API协议也可使用,但个人未全部测试,如果你使用了其他平台,欢迎反馈测试结果。 - - 新增:现在每种图床可以保存最多26组备用配置,可一键替换默认图床配置,方便在不同的图床目录或例如在不同的S3兼容平台之间切换。 - - 备用配置保存于本地,不会上传到云端。 - - 可将备用配置导出为json格式并复制到剪贴板内,并可将剪贴板内的json格式的备用配置导入到APP内,方便保存和同步。 - - 修改配置时可导入现有默认配置,方便快速修改。 - - 优化:检查图床配置时现在会显示loading界面,避免误认为无响应。 - - 修复:修复了腾讯云、阿里云、七牛云和又拍云在获取文件列表时,单目录下获取文件数量存在上限,导致不能获得全部文件列表的问题。 - - 修复:修复了又拍云最多只能获取到25个存储桶的问题。 - - 修复:修复了每次进入目录时都会检查一次存储桶是否为空的问题,大幅提高了目录文件的加载速度。 - - 修复:修复了ftp管理起始目录如果不以'/'结尾导致报错的问题。 - - 修复:修复了smms数据库更新数据的SQL语句错误导致无法更新图床配置的问题。 - -- 2022-11-16: **V1.90**: - - - 新增:新增了对**Imgur**管理功能的支持。 - - 图片查看时使用CDN进行了加速,避免图片加载过慢或失败。 - - 新增:新增了对**FTP**和**SSH/SFTP**上传、相册、管理功能的支持,可匿名或SSH登录服务器管理文件,另外作为补充,内置了**SSH终端**,可直接管理**云服务器**。 - - 由于一般情况下普通FTP功能限制较多,能做到的操作不多,因此管理功能仅支持SFTP方式。 - - 禁止了对文件名包含 `*#?` 等特殊字符的文件的删除操作,防止出现意外情况。 - - 由于服务器操作的敏感性,APP内仅实现了一些安全操作,其它操作请使用内置**SSH终端**进行。 - - 为了解决FTP文件上图片无法直接预览的问题,在上传时会自动生成一份图片缩略图保存于缓存目录下用于相册内查看,在从相册中删除图片时会自动删除缓存目录下的缩略图。 - - 缩略图保存路径为`手机存储/Android/data/com.example.horopic/cache/ftp/`,可根据需要自行删除。 - - 管理功能针对普通目录和文件,为避免混乱,不支持软链接等特殊类型文件,如需管理软链接等特殊类型文件,请使用内置**SSH终端**。 - - 支持选择管理功能入口目录,方便快速进入指定目录。 - - 新增:Github和SSH/SFTP管理页面现在支持预览markdown文件。 - - 新增:图床配置导出增加了**导出全部**的选项。 - - 新增:修改了图片上传和删除时保存配置信息的逻辑,现在会在本地数据库保存每一张图片上传时对应的图床配置,这样即使上传后修改了图床配置,仍然可以正常删除云端图片,同时为后续的一个图床多套配置功能做准备。 - - 新增:上传页面和相册页面顶部栏增加了配置按钮,方便快速修改常用配置。 - - 优化:现在浏览本地文件时如果目标已被删除会显示空白提示页面。 - - 优化:一些UI优化,包括如下: - - 本地图片预览修改为页面居中显示。 - - 首页和相册页面的图床选择弹出栏的的顺序调整为图床首字母字典序。 - - 图床配置和默认图床选择页面的顺序调整为图床首字母字典序。 - - 更新日志界面现在支持跳转链接和选择文字。 - - 修复:修复了在配置页面修改默认图床和单独图床配置页面内设置为默认图床后,上传页面弹出栏显示的默认图床和相册页面显示的图床没有同步更新的问题。 - - 修复:修复了兰空图床管理页面内删除相册后,后续的部分文件会被错误显示为文件夹的问题。 -- 2022-11-12: **V1.89**: - - - 新增:新增了对**Github**管理功能的支持,并且可以浏览其它用户的公开仓库,同时可以下载其它用户的公开仓库的文件,此外复制链接时对私有仓库还会添加临时访问token,以便于下载私有仓库的文件。 - - 新增:使用github图床时,如果未设置自定义域名,现在相册预览,文件下载等情况下会默认使用加速服务,以解决国内可能无法访问raw.githubusercontent.com,导致图片无法显示或者下载失败的问题。 - - 新增:新增了近200个文件图标,使得文件管理界面更加美观。 - - 修复:将上传界面的同时可进行任务数修改为1,以解决Github同时上传冲突的上传失败问题。 -- 2022-11-09: **V1.88**: - - - 新增:**由于新增了字段,旧版本APP保存兰空图床配置会失败,请尽快更新到最新版本** - - 新增:新建了软件的介绍和配置说明网站[https://pichoro.horosama.com](https://pichoro.horosama.com),并在软件配置主页加入了`软件主页`跳转选项 - - 新增:兰空图床显示了当前token,同时在已有token的情况下,可以直接获取策略ID和相册ID列表,不再需要输入用户名和密码。 - - 新增:兰空图床配置参数增加了相册ID,管理界面上传时也会上传到对应相册,但限于以下两种情况下才会生效: +## 2023-09-17 **V2.2.2** + +- 新增:又拍云现在支持设置防盗链token和过期时间参数 +- 新增:现在首页标题会显示当前的默认上传图床 +- 新增:时间戳重命名现在不再额外添加5个随机字符 +- 新增:自定义重命名中的时间戳选项现在采用毫秒,而不是秒 +- 新增:移除了二维码导入配置时的校验步骤以提高导入速度 +- 新增:现在保存图床设置时不再强制进行校验(与`校验当前配置`功能重复) +- 新增:现在拷贝链接时,会对文件名进行basename处理 +- 新增:现在查看配置时密码不再加密显示,方便配置 +- 修复:修复了通过拍照上传时,文件名中含有的二级路径没有生效的问题 +- 修复:修复了文件名中含有二级路径时,开启图片压缩后无法上传的问题 +- 修复:imgur移除了失效的CDN加速网址 +- 修复:修复了文件名中含有二级路径时,部分图床无法远程删除图片的问题 +- 修复:修复了从二维码导入时,路径设置为根目录时导入错误的问题 +- 维护:重构了大部分代码以提高性能和可维护性 + +## 2023-08-26 **V2.1.3** + +- 依赖:从Flutter 3.3版本迁移至 3.13 版本 +- 优化:移除了底部栏的文字标签 +- 更新:阿里云存储桶区域现在支持无地域属性 +- 更新:腾讯云移除了不再支持的莫斯科区域 +- 更新:FTP现在删除功能会额外校验路径,避免误删除 + +## 2023-07-25 **V2.1.2** + +- 修复:修复了imgur仓库无法进入的问题 + +## 2023-06-20 **V2.1.1** + +- 更新:更新了alist驱动列表,与最新版(3.21.0)保持同步 + +## 2023-06-20 **V2.1.0** + +- webdav现在支持设置自定义域名 +- ftp现在支持设置自定义域名 + +## 2023-05-04 **V2.0.0** + +- 移除了用户登录和云端同步系统,现在所有数据保存于用户本地 +- 移除了imgur管理登录页面对clientsecret的需求 +- 更新了alist驱动列表,与最新版(3.16.3)保持同步 +- 修复了重复设置Alist为默认图床时,默认相册设置错误的问题 + +## 2023-04-14 **V1.11.0** + +- 维护:更换了github加速代理。 +- 修复:修复了复制链接时如果链接中有逗号会导致格式错误的问题。 +- 修复:修复了alist复制链接格式错误的问题。 + +## 2023-02-28 **V1.10.0** + +- 新增:s3/阿里云/腾讯云等平台现在可以单独为存储桶设置自定义域名了。 +- 新增:现在会在安装或者启动时获取安装未知应用权限,避免APP无法启动。 +- 维护:部分代码精简 +- 修复:修复了s3平台文件地址错误的问题。 +- 修复:修复了图片缓存导致相同地址的图片无法更新的问题。 + +## 2023-01-05 **V1.9.9** + +- 新增:更换了版本升级使用的下载地址为cdn加速地址,优化了下载速度。 +- 新增:将作者QQ二维码修改为软件反馈交流群二维码。 +- 维护:精简了部分通用代码,方便后续维护和功能扩展,共减少约8000行冗余代码。 +- 修复:修复了设置主题为自动后,APP无法正常启动的问题。 +- 修复:修复了在上传下载页面删除上传任务后,使用 `全部开始`按钮仍然会上传已删除的任务的问题。 +- 修复:修复了在进入webdav上传下载页面时,自动切换标签页没有正常作用的问题。 + +## 2022-12-18 **V1.9.8** + +- 新增:添加了图片压缩功能,现在可以选择在上传图片前先进行压缩了,可选压缩后格式为jpg、png和webp,并且可以自定义最小宽度、最小高度和压缩后质量。 +- 新增:S3 API兼容平台上传时现在会主动修改content-type。 +- 优化:添加了webp文件格式的图标。 + +## 2022-12-05 **V1.9.7** + +- 新增:添加了对**WebDAV**的支持,使用坚果云webdav和Alist V3的webdav测试通过。 +- 新增:Alist V3现在可以不登录访问了,只需要设置Alist域名,即可在管理页面中查看文件。 +- 修复:修复了清空相册数据库页面没有显示阿里云的问题。 + +## 2022-12-02 **V1.9.6** + +- 新增:新增了对[Alist V3](https://alist.nn.ci/zh/)的支持,现在可以通过Alist间接管理其支持的各种存储和网盘。支持的平台包括:本地存储,阿里云盘,百度网盘,夸克网盘,蓝奏云,谷歌相册,115,OneDrive,天翼云盘,GoogleDrive,123云盘,FTP / SFTP,PikPak,S3,又拍云,WebDav,Teambition,分秒帧,移动云盘等等。 + - Alist的token有有效期,PicHoro会每天尝试更新一次token,如果遇到错误,请尝试重新设置Alist来更新token。 + - 支持通过Alist来查看图片和观看视频。 + - 支持快捷操作Alist存储,包括新增,卸载,修改配置,启用,禁用等。 + - 支持上传/下载,新建文件夹/重命名/删除/分享链接等各种操作,使用百度网盘官方接口时,会自动添加 `User-Agent:pan.baidu.com`的请求头。 + - 由于Alist支持的平台比较多,个人一些平台没有账号,未能全部完成测试,如果发现有问题,请提交issue。已经测试过的平台有: + - Alist V3 + - 阿里云盘 + - 百度网盘 + - 本机存储 + - SFTP + - FTP + - 一刻相册 + - WebDav(使用坚果云webdav测试) +- 新增:增加了对**PDF**格式文件在线预览的支持,可以搜索和跳转页面。 +- 新增:增加了对**音视频文件**在线播放的支持,其中: + - mp4, flv,m4v,mp3,avi,mpg,flac,ogg,ts,aac,m4a,vob等格式支持播放列表功能,列表由当前目录下的视频文件组成。 + - mkv和rmvb等格式由于后台实现的问题,暂时不支持播放列表功能。 + - mkv文件可以自动识别和加载字幕文件,字幕文件需要与视频的文件名相同,后缀名为srt/ass/vtt/sbv/ttml/dfxp/ssa之一。 + - **为了解码mkv等格式,添加了VLC依赖,包体积增加较多,我在尝试缩减和考虑是否提供不带VLC的版本。** +- 新增:现在支持**更多的文本文件格式**预览,涵盖了各种常见编程语言源文件。 +- 新增:图床管理页面增加了重置排序按钮,可以将图床的排序重置为默认排序。 +- 优化:压缩了部分assets文件,尽量减少对包体积的影响,同时删除了部分未使用的assets文件。 +- 优化:更换了网页浏览的实现方式。 +- 优化:用户登录后的图床信息查看页,现在按照图床名称排序。 +- 优化:优化部分UI尺寸。 +- 优化:优化了部分冗余代码。 +- 修复:修复了图片预览时会在最后额外添加一页空白页的问题 +- 修复:修复了注销登录时没有清空S3上传/下载列表的问题。 +- 修复:修复了连续上传模式下,使用S3上传时相册显示不正确的问题。 +- 修复:修复了图床设置为S3时,相册内删除会错误的将url当做本地文件名进行删除的问题。 +- 修复:修复了使用S3时,返回的图片URL不包括存储桶,导致图片无法显示的问题。 +- 修复:修复了图床管理页面的排序问题。 + +## 2022-11-25 **V1.9.5** + +- 新增:设置界面现在在有新版本时会显示新版本号,且 `检查更新`会变为 `有新版本`来提示用户。 +- 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 +- 优化:调整了上传/下载界面和主页上传框任务列表的顺序,现在后添加的任务会显示在最上面。 +- 优化:调整了上传界面和主页上传框任务列表的UI,进度条修改到任务名称的右侧,减少单个任务的宽度。 +- 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 +- 优化:修改了部分界面UI。 +- 修复:修复了在上传界面和主页上传框内,点击全部上传时,已上传完成的任务会被重新上传的问题。 +- 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 +- 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 + +## 2022-11-24 **V1.9.4 beta 2** + +- 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 +- 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 +- 优化:修改了部分界面UI。 +- 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 + +## 2022-11-24 **V1.9.4 beta 1** + +- 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 + +## 2022-11-22 **V1.9.3** + +- 新增:现在进入联系作者界面时会自动复制作者QQ号到剪贴板。 +- 优化:修改了连续上传模式的逻辑,由 `拍照->校验->上传->拍照`改为 `拍照->校验(异步上传)->拍照`,将上传操作修改为异步执行,提高用户体验。 +- 优化:修改了部分提示信息。 +- 优化:解决了上传界面和相册界面的右上角设置弹出栏,必须点击文字部分才能弹出窗口的问题。 +- 优化:优化了APP的启动速度。 +- 修复:修复了aws上传和下载列表会在APP关闭后被清空的问题。 +- 修复:修复了批量复制链接时,从第二行开始,开头会多一个空格的问题。 + +## 2022-11-21 **V1.9.2** + +- 优化:更换了github和七牛云的图标,优化在深色模式下的显示效果。 +- 修复:修复了登录时没有同步FTP和S3平台配置的问题。 +- 修复:修复了注销登录后会导致图床管理页面无法正常显示的问题。 + +## 2022-11-19 **V1.9.1** + +- 新增:增加了对**兼容S3 API协议的平台**的支持,目前已测试通过的平台包括AWS S3,backblaze和cloudfare R2,其他平台只要兼容S3 API协议也可使用,但个未全部测试,如果你使用了其他平台,欢迎反馈测试结果。 +- 新增:现在每种图床可以保存最多26组备用配置,可一键替换默认图床配置,方便在不同的图床目录或例如在不同的S3兼容平台之间切换。 + - 备用配置保存于本地,不会上传到云端。 + - 可将备用配置导出为json格式并复制到剪贴板内,并可将剪贴板内的json格式的备用配置导入到APP内,方便保存和同步。 + - 修改配置时可导入现有默认配置,方便快速修改。 +- 优化:检查图床配置时现在会显示loading界面,避免误认为无响应。 +- 修复:修复了腾讯云、阿里云、七牛云和又拍云在获取文件列表时,单目录下获取文件数量存在上限,导致不能获得全部文件列表的问题。 +- 修复:修复了又拍云最多只能获取到25个存储桶的问题。 +- 修复:修复了每次进入目录时都会检查一次存储桶是否为空的问题,大幅提高了目录文件的加载速度。 +- 修复:修复了ftp管理起始目录如果不以'/'结尾导致报错的问题。 +- 修复:修复了smms数据库更新数据的SQL语句错误导致无法更新图床配置的问题。 + +## 2022-11-16 **V1.9.0** + +- 新增:新增了对**Imgur**管理功能的支持。 + - 图片查看时使用CDN进行了加速,避免图片加载过慢或失败。 +- 新增:新增了对**FTP**和**SSH/SFTP**上传、相册、管理功能的支持,可匿名或SSH登录服务器管理文件,另外作为补充,内置了**SSH终端**,可直接管理**云服务**。 + - 由于一般情况下普通FTP功能限制较多,能做到的操作不多,因此管理功能仅支持SFTP方式。 + - 禁止了对文件名包含 `*#?` 等特殊字符的文件的删除操作,防止出现意外情况。 + - 由于服务器操作的敏感性,APP内仅实现了一些安全操作,其它操作请使用内置**SSH终端**进行。 + - 为了解决FTP文件上图片无法直接预览的问题,在上传时会自动生成一份图片缩略图保存于缓存目录下用于相册内查看,在从相册中删除图片时会自动删除缓存目下的缩略图。 + - 缩略图保存路径为 `手机存储/Android/data/com.example.horopic/cache/ftp/`,可根据需要自行删除。 + - 管理功能针对普通目录和文件,为避免混乱,不支持软链接等特殊类型文件,如需管理软链接等特殊类型文件,请使用内置**SSH终端**。 + - 支持选择管理功能入口目录,方便快速进入指定目录。 +- 新增:Github和SSH/SFTP管理页面现在支持预览markdown文件。 +- 新增:图床配置导出增加了**导出全部**的选项。 +- 新增:修改了图片上传和删除时保存配置信息的逻辑,现在会在本地数据库保存每一张图片上传时对应的图床配置,这样即使上传后修改了图床配置,仍然可以正常除云端图片,同时为后续的一个图床多套配置功能做准备。 +- 新增:上传页面和相册页面顶部栏增加了配置按钮,方便快速修改常用配置。 +- 优化:现在浏览本地文件时如果目标已被删除会显示空白提示页面。 +- 优化:一些UI优化,包括如下: + - 本地图片预览修改为页面居中显示。 + - 首页和相册页面的图床选择弹出栏的的顺序调整为图床首字母字典序。 + - 图床配置和默认图床选择页面的顺序调整为图床首字母字典序。 + - 更新日志界面现在支持跳转链接和选择文字。 +- 修复:修复了在配置页面修改默认图床和单独图床配置页面内设置为默认图床后,上传页面弹出栏显示的默认图床和相册页面显示的图床没有同步更新的问题。 +- 修复:修复了兰空图床管理页面内删除相册后,后续的部分文件会被错误显示为文件夹的问题。 + +## 2022-11-12 **V1.8.9** + +- 新增:新增了对**Github**管理功能的支持,并且可以浏览其它用户的公开仓库,同时可以下载其它用户的公开仓库的文件,此外复制链接时对私有仓库还会添加临访问token,以便于下载私有仓库的文件。 +- 新增:使用github图床时,如果未设置自定义域名,现在相册预览,文件下载等情况下会默认使用加速服务,以解决国内可能无法访问raw.githubusercontent.com导致图片无法显示或者下载失败的问题。 +- 新增:新增了近200个文件图标,使得文件管理界面更加美观。 +- 修复:将上传界面的同时可进行任务数修改为1,以解决Github同时上传冲突的上传失败问题。 + +## 2022-11-09 **V1.8.8** + +- 新增:**由于新增了字段,旧版本APP保存兰空图床配置会失败,请尽快更新到最新版本** +- 新增:新建了软件的介绍和配置说明网站[https://pichoro.horosama.com](https://pichoro.horosama.com),并在软件配置主页加入了 `软件主页`跳转选项 +- 新增:兰空图床显示了当前token,同时在已有token的情况下,可以直接获取策略ID和相册ID列表,不再需要输入用户名和密码。 +- 新增:兰空图床配置参数增加了相册ID,管理界面上传时也会上传到对应相册,但限于以下两种情况下才会生效: 1. 基于付费企业版兰空图床搭建 2. 开源免费版需要自己或者联系管理员修改源代码文件,修改方式为打开 **/app/Services/ImageService.php**文件,修改第139行,原文件为 ```php - // 图片保存至默认相册(若有) if ($albumId = $user->configs->get(UserConfigKey::DefaultAlbum)) { if ($user->albums()->where('id', $albumId)->exists()) { $image->album_id = $albumId; @@ -224,7 +224,6 @@ if ($request->has('album_id')) { $image->album_id = $request->input('album_id'); } else { - // 图片保存至默认相册(若有) if ($albumId = $user->configs->get(UserConfigKey::DefaultAlbum)) { if ($user->albums()->where('id', $albumId)->exists()) { $image->album_id = $albumId; @@ -233,184 +232,223 @@ } ``` -- - 新增:APP启动时现在会自动清理已下载的新版本安装包,避免占用过多空间。 - - 优化:修改了图床配置界面的图标UI。 - - 修复:修复了兰空图床二维码扫描没有反应的问题,感谢知乎用户@力子头的反馈 -- 2022-11-08: **V1.87**: - - 新增:图床仓库管理功能增加了对**兰空图床**的支持 - - 优化:修改了相册和文件浏览页面缩略图的显示方式,从cover修改为fill。 - - 修复:修复了如果没有先单独下载一次文件,直接全部下载时,无法正常创建下载目录导致下载失败的问题。 -- 2022-11-08: **V1.86**: - - 新增:图床仓库管理功能增加了对**七牛云**的支持 - - 新增:图床管理功能文件浏览新增了查看文件详情页,可查看文件的详细信息。 - - 新增:现在图床设置页面会自动填充已保存的配置信息,方便快速查看和更改配置。 - - 新增:阿里云新建文件夹时,加入了对文件夹名的预检查和处理,自动去除开头和结尾的'/',避免创建失败。 - - 优化:修改了部分UI的表现使其更加美观。 - - 修复:修复了七牛云亚太首尔地区上传api路径错误的问题。 - - 修复:修复了七牛云删除云端文件时,如果路径设置为根目录,会导致删除失败的问题。 - - 修复:修复了阿里云OSS上传文件时,如果路径设置为根目录,会导致上传失败的问题。 - - 修复: 修复了管理功能浏览文件时,网络错误会导致界面卡在loading的问题。 - - 修复:修复了部分界面UI错误,修复了日志界面的一些显示问题。 -- 2022-11-04: **V1.85**: - - 新增:图床仓库管理功能增加了对**又拍云**的支持,需要单独登录一次又拍云的账号密码,实现了服务账号管理、存储桶管理到文件管理的完整支持。 - - 新增:重构了下载页面,现在同时显示上传和下载任务,并且现在重新进入上传/下载页面不会丢失任务了,并且现在可以单独删除任务了。 - - 优化:上传界面内用户未登录时,现在会提示用户登录后才能使用相关功能。 - - 优化:相册内删除图片时,如果云端删除失败,现在会提示用户并中止删除流程。 - - 优化:现在设置又拍云图床时,网站后缀不再是必选参数。 - - 优化:现在部分页面返回后会主动触发上级页面刷新,以保证数据的及时更新。 - - 优化: 目录内文件全部删除后现在正确显示空目录提示背景,而不是空白。 - - 优化:优化了注销登录的处理,现在会将所有用户设置重置为默认值。 - - 优化:修改了部分图标和文字,以及部分页面的布局。 - - 修复:修复了相册页面,来回切换图床会导致部分图床无法正常删除云端图片的问题。 - - 修复:修复了当图片路径中包含中文时,又拍云图床无法正常删除云端图片的问题。 - - 修复:修复了又拍云图床上传图片后,图片链接多了一个'/'的问题。 - - 修复:修复了未登录或者配置图床的时候,对应图床管理页面会卡住的问题。 - - 修复:修复了从剪贴板上传文件功能在部分情况下无法正常使用的问题。 - - 修复:修复了部分日志函数名记录错误的问题。 -- 2022-11-01: **V1.84**: - - 新增:图床仓库管理功能增加了对**阿里云**的支持。 - - 新增:修改了上传文件时重命名的逻辑,现在不会同步重命名本地文件了。**感谢@Yurzi的建议**。 - - 新增:自定义文件重命名现在增加了异常处理,并且由于重命名逻辑的修改,现在可以使用'/'来同步新建文件夹了。**感谢@Yurzi的建议**。 - - 新增:增加了异常错误的日志记录和查看功能,并支持导出为txt文件和同步复制到剪贴板。**感谢@Yurzi的建议**。 - - 新增:优化了用户注册时用户名和密码的输入规则,现在不强求必须是8位纯数字了,仅要求不包括空白字符,同时优化了不合法输入的提示信息。**感谢@chancat87的建议**。 - - 新增:增加了当用户密码不是8位纯数字时的加密和解密规则,已注册用户不受影响。 - - 新增:上传界面从网络链接获取图片时,加入了loading窗口提示,防止用户误以为程序卡死。 - - 优化:修改了部分窗口的提示语使其更加清晰。 - - 修复:修复了在图床管理界面,从剪贴板获取文件的时候,链接中带有?查询字符串时,无法正确获取文件名的问题。 - - 修复:修复了图床管理文件浏览界面,按文件大小排序时,排序结果不正确的问题。 -- 2022-10-27: **V1.83**: - - 新增:上传页面重新设计,将主要功能放在了浮动按钮上,主页面用来显示上传列表,避免上传照片比较多时,一直卡在没有进度提示的loading窗口,单张拍照和连续上传两个功能仍沿用旧的上传方式。 - - 新增:用户登录页面重新设计,现在分为注册/登录和已登录两个页面,同时已登录页面显示用户信息和全部图床配置信息,并可以拉取云端配置和注销登录。 - - 新增:相册页面现在在切换页面的时候,会保留当前的页面状态,包括页数,选中状态等,同时上传了新图片或者清空了相册数据库后会自动触发相册刷新。 - - 新增:系统状态栏颜色调整为透明色,同时优化了部分页面APPBar的显示效果。 - - 优化:腾讯云COS二级页面的文件底部弹出栏,显示文件名时不会再显示目录前缀了。 - - 优化:部分文本显示现在可以被复制。 - - 修复:图床存储路径为一串空白字符时会导致上传错误的问题。 - - 修复:Github图床相册预览无法显示照片的问题和复制的url无法直接显示的问题。 -- 2022-10-27: **V1.82**: - - 新增:上传页面默的认图床切换列表现在会使用不同的颜色来区分当前的默认图床。 - - 优化:弹出框统一为Cupertino样式。 - - 优化: 网络图片预览加入了加载中和加载失败的状态管理。 - - 优化:重命名了大部分代码文件和部分变量名,使其符合dart命名规范。 - - 优化:解决了绝大部分代码格式不规范问题,共计约450处。 - - 优化:优化了代码结构,提取出了一些公共方法,精简了代码量,共减少约2000行代码。 - - 修复:清空相册数据库页面,弹出框点击确认后,弹出框不会消失的问题。 -- 2022-10-25: **V1.81**: - - - 新增:图床仓库管理功能增加了对**SM.MS**的支持 - - 新增:图床管理主页卡片现在可以拖动排序了,拖动后会自动保存顺序。 - - 优化: 文件全部删除后显示空目录提示背景,而不是空白。 - - 优化: 调整了部分界面里浮动按钮的位置和大小。 - - 修复:网络不佳的情况下,提前退出页面后,请求回调setState报错。 - - 修复: 如果在本地文件浏览目录里删除了已下载的文件,返回下载页面后删除下载任务会报错的问题。 - - 修复: 本地下载文件目录浏览页面里,右滑删除按钮点击无效的问题。 - - 修复: 图床文件浏览页面中,删除文件的时候,选中列表对应元素没有被同步移除的bug。 -- 2022-10-24: **V1.80**: - - - 增加了新的功能**图床仓库管理**,目前实现了对**腾讯云COS**的支持(*相当于内置了一个精简版的腾讯COSBrowser*),后续会陆续增加对其他图床的支持,主要功能有: - 1. 查看存储桶列表,支持创建和删除,修改访问权限,设置存储桶为腾讯云COS的默认存储桶。 - 2. 查看存储桶中的目录和文件列表,目录和文件都支持右滑删除,文件额外支持右滑分享连接。可新建文件夹,可设置目录为图床仓库的默认目录,文件可复制多种格式的链接,重命名,照片可以预览。 - 3. 存储桶管理页面和本地下载目录浏览页面均可按修改时间,文件名,文件大小,文件类型等排序。 - 4. 支持多选上传文件和照片,上传剪贴板内的网络链接。 - 5. 可批量下载和管理已下载文件,支持暂停和继续下载,支持删除已下载文件。 - 6. 支持管理本地下载目录,可重命名和删除已下载文件,支持预览照片和调用其它应用打开文件。 - - 修改了本地相册数据库的存储路径 - - 从剪贴板链接获取网络图片时,加入了对空文本的处理 - - 修改了部分字体,部分弹出框修改为ios样式 - - UI细节优化 - - 修复了图床api在请求失败时的错误处理 -- 2022-10-17: **V1.76**: - - 重写了路由管理,优化了路由跳转的体验,修改了跳转动画。 - - 主页/相册/设置页面现在不会左上角出现返回按钮,更加美观。 - - 优化了主页在深色主题下的显示效果 -- 2022-10-15: **V1.75**: - - 增加了对阿里云OSS的支持 - - 增加了对又拍云存储的支持 - - 增加了导出图床配置到剪贴板的功能,导出格式为json - - 增加了从剪贴板中的图片链接直接获取图片的功能,并且可以通过换行符分隔多个图片链接来一次性获取多张图片 - - 增加了上传的时候自定义文件名的功能,使用`{Y}`、`{y}`、`{m}`、`{d}`、`{uuid}`、`{md5}`等占位符,可选年月日,uuid,md5,随机字符串等任意组合来自定义文件名。 - - 增加了手动清除缓存的功能 -- 2022-10-14: **V1.70**: - - 增加了对腾讯云COS的支持 - - 修改自定义链接格式的占位符为`$fileName`和`$url`,来和PicGo保持一致,同时修改了默认的自定义链接格式。 - - 修复了七牛云不设置存储路径的时候保存路径错误的问题。 - - 修复了github,imgur和七牛云导入二维码配置的时候可选参数默认值错误和七牛云数据库保存的数据错误的问题。 -- 2022-10-13: **V1.65**: - - 增加了对七牛云的支持(吐槽一下,七牛云这官方文档真的是emmmm) - - 调整了配置和图片上传/删除的时候的响应和连接超时时间设置数值。 - - 相册图片的外框默认透明色,选中的时候会有一个红色的边框提示。 -- 2022-10-12: **V1.60**: - - 增加了对Imgur图床的支持,但是由于Imgur的限制,使用的时候需要配置手机代理,在个人手机上配合clash测试可用。 - - 加入了设置配置和图片上传/删除的时候的响应和连接超时时间设置,防止网络不好的情况下卡死。 - - 区分了相册显示的时候的图片地址和复制的时候的图片地址,改善相册加载图片的速度,例如兰空图床在相册小图中显示的是缩略图,预览大图的时候才会加载原图。 - - 修复了设置页面跳转到主页的时候,有时会先跳转到相册页面的问题 - - 修复了注册用户的时候,同步创建本地相册数据库的代码没有执行的bug - - 修复了连续上传功能中,复制的链接的格式错误的bug - - 更改了登录页面UI,方便区分出是否已经登录 -- 2022-10-11: **V1.55**: - - 增加了扫码导入PicGo配置的功能,和PicGo进一步兼容。 - - 增加了对github图床的支持,在主页增加了切换默认上传图床的浮动按钮。 - - 增加了自定义复制链接的格式的功能,和PicGo的自定义格式一样,使用\${url}和\${filename}来表示链接和文件名,可以在设置中自定义。 - - 增加了新的设置选项,可以选择在删除图片的时候是否同步删除网络端的图片(默认不删除)。 - - 重新整理了源代码文件架构,使得代码更加清晰,方便后续的更新和维护。 - - 修复了相册上翻页功能没有按预期作用的问题。 - - 修复了图片多选的时候,删除功能的bug,同时优化了动画显示,现在不会傻傻的等待了。 - - 修复了相册中显示的图床和默认上传图床不一致的时候无法删除网络端图片的bug。 - - 修复了选中状态会在翻页的时候保留的bug。 - - 修复了设置默认上传图床参数的时候,没有同步更改云端数据库记录的问题。 - - 优化了界面UI,修复了一些组件尺寸的问题。 -- 2022-10-09: **V1.50**: - - 增加了**相册**功能,进一步对标了PicGo,现在PicHoro不仅是一款上传工具,也是一款图床管理工具。 - - 相册模块中实现了这些功能: - 1. 显示已经上传的图片,分页显示,每页12张,可以上划和下拉翻页。 - 2. 分图床显示,可选择显示某个图床的图片。 - 3. 实现了删除图片的功能,删除后会自动刷新相册,默认删除数据库记录和图床上的图片,可选择是否同步删除本地图片。 - 4. 实现了多选功能,可选择多张图片进行删除和复制指定格式的链接。 - 5. 可点击图片查看大图,双击图片可复制指定格式的链接,长按图片可弹出菜单,选择链接格式或者删除图片。 - 6. 相册中的图片数据保存在本地,通过APP内升级时不会丢失。 - - 增加了选择默认图床的页面,可以更直观的知道当前默认图床是哪个。 - - 修复了一些已知的bug。 -- 2022-10-07: **V1.41**: - - 增加了对SM.MS图床的支持 - - 修复了markdown链接的文件名错误的问题 -- 2022-10-07: **V1.40**: - - - 增加了文件上传自动重命名的功能 - - 增加了文件上传后自动复制链接的功能,同时可选url,html,markdown,bbcode和带链接的markdown等格式 - - 增加了软件APP内自动更新的功能 - - Github国内打开太慢,把项目地址页面换成更新日志页面 - - 部分bug修复 -- 2022-10-06: **V1.31**: - - - 修复了已注册用户在新设备第一次登录的时候,无法正常登录的bug - - 修复了连续上传功能在退出的时候会卡在上传中的bug - - 已登录的设备在获取云端配置的时候不需要重新输入用户名和密码了 - - 修复了部分代码小bug -- 2002-10-05: **V1.30**: - - - 重构了整个APP的代码架构,把所有的页面和功能性函数都放在了对应文件夹里,方便后续的维护和扩展,同时抽象了上传等功能的接口,后续增加图床时可以直接调用。 - - 增加了用户登录和拉取云端配置的功能,可以通过用户名和密码登录,将图床配置等保存在服务器上,这里现在用户本地用3DES加密然后再保存到数据库,除了用户名和图床名是明文外,其他的都是密文,这样可以保证用户的隐私。 - - 增加了软件主题切换的功能,增加了软件更新页面 - - 新设计了软件的图标和启动画面,同时对软件的UI进行了一些优化 - - 一些BUG修复 -- 2002-10-04: **V1.21**: - - - 增加了上传图片和配置图床时的等待动画 - - 在设置页面增加了底部导航栏,修改了部分按钮的名字 - - 调整了部分弹出式提示框的实现方式,修改为自动消失的小提示框,同时部分重要提示框禁止了点击背景消失 - - 修复了项目地址页面打不开的问题 - - 优化了部分代码 -- 2002-10-03: **V1.20**: - - - 现在从相册里选择照片的时候可以多选了,上传功能也更新为批量上传 - - 增加了新的设置页面,可以在设置页面里选择图床配制,项目地址和联系作者 - - 重构了部分代码,为后续增加图床平台做准备 - - 优化了页面布局,改变了部分UI -- 2002-10-03: **V1.10**: - - - 增加了对权限的主动获取,避免用户手动授予权限 - - 增加了对拍照后自动上传并返回拍照界面的功能 - - 增加了对各种异常的提醒 - - 优化了弹出框的显示布局 - - 优化了页面布局 -- 2022-10-02: **V1.00**: 项目初始化,完成基本的上传功能,目前仅支持兰空图床,需要手动授予存储和相机权限 +- 新增:APP启动时现在会自动清理已下载的新版本安装包,避免占用过多空间。 +- 优化:修改了图床配置界面的图标UI。 +- 修复:修复了兰空图床二维码扫描没有反应的问题,感谢知乎用户@力子头的反馈 + +## 2022-11-08 **V1.8.7** + +- 新增:图床仓库管理功能增加了对**兰空图床**的支持 +- 优化:修改了相册和文件浏览页面缩略图的显示方式,从cover修改为fill。 +- 修复:修复了如果没有先单独下载一次文件,直接全部下载时,无法正常创建下载目录导致下载失败的问题。 + +## 2022-11-08 **V1.8.6** + +- 新增:图床仓库管理功能增加了对**七牛云**的支持 +- 新增:图床管理功能文件浏览新增了查看文件详情页,可查看文件的详细信息。 +- 新增:现在图床设置页面会自动填充已保存的配置信息,方便快速查看和更改配置。 +- 新增:阿里云新建文件夹时,加入了对文件夹名的预检查和处理,自动去除开头和结尾的'/',避免创建失败。 +- 优化:修改了部分UI的表现使其更加美观。 +- 修复:修复了七牛云亚太首尔地区上传api路径错误的问题。 +- 修复:修复了七牛云删除云端文件时,如果路径设置为根目录,会导致删除失败的问题。 +- 修复:修复了阿里云OSS上传文件时,如果路径设置为根目录,会导致上传失败的问题。 +- 修复: 修复了管理功能浏览文件时,网络错误会导致界面卡在loading的问题。 +- 修复:修复了部分界面UI错误,修复了日志界面的一些显示问题。 + +## 2022-11-04 **V1.8.5** + +- 新增:图床仓库管理功能增加了对**又拍云**的支持,需要单独登录一次又拍云的账号密码,实现了服务账号管理、存储桶管理到文件管理的完整支持。 +- 新增:重构了下载页面,现在同时显示上传和下载任务,并且现在重新进入上传/下载页面不会丢失任务了,并且现在可以单独删除任务了。 +- 优化:上传界面内用户未登录时,现在会提示用户登录后才能使用相关功能。 +- 优化:相册内删除图片时,如果云端删除失败,现在会提示用户并中止删除流程。 +- 优化:现在设置又拍云图床时,网站后缀不再是必选参数。 +- 优化:现在部分页面返回后会主动触发上级页面刷新,以保证数据的及时更新。 +- 优化: 目录内文件全部删除后现在正确显示空目录提示背景,而不是空白。 +- 优化:优化了注销登录的处理,现在会将所有用户设置重置为默认值。 +- 优化:修改了部分图标和文字,以及部分页面的布局。 +- 修复:修复了相册页面,来回切换图床会导致部分图床无法正常删除云端图片的问题。 +- 修复:修复了当图片路径中包含中文时,又拍云图床无法正常删除云端图片的问题。 +- 修复:修复了又拍云图床上传图片后,图片链接多了一个'/'的问题。 +- 修复:修复了未登录或者配置图床的时候,对应图床管理页面会卡住的问题。 +- 修复:修复了从剪贴板上传文件功能在部分情况下无法正常使用的问题。 +- 修复:修复了部分日志函数名记录错误的问题。 + +## 2022-11-01 **V1.8.4** + +- 新增:图床仓库管理功能增加了对**阿里云**的支持。 +- 新增:修改了上传文件时重命名的逻辑,现在不会同步重命名本地文件了。**感谢@Yurzi的建议**。 +- 新增:自定义文件重命名现在增加了不合规格式的检查,并且由于重命名逻辑的修改,现在可以使用'/'来同步新建文件夹了。**感谢@Yurzi的建议**。 +- 新增:增加了异常错误的日志记录和查看功能,并支持导出为txt文件和同步复制到剪贴板。**感谢@Yurzi的建议**。 +- 新增:优化了用户注册时用户名和密码的输入规则,现在不强求必须是8位纯数字了,仅要求不包括空白字符,同时优化了不合法输入的提示信息。**感谢@chancat8的建议**。 +- 新增:增加了当用户密码不是8位纯数字时的加密和解密规则,已注册用户不受影响。 +- 新增:上传界面从网络链接获取图片时,加入了loading窗口提示,防止用户误以为程序卡死。 +- 优化:修改了部分窗口的提示语使其更加清晰。 +- 修复:修复了在图床管理界面,从剪贴板获取文件的时候,链接中带有?查询字符串时,无法正确获取文件名的问题。 +- 修复:修复了图床管理文件浏览界面,按文件大小排序时,排序结果不正确的问题。 + +## 2022-10-27 **V1.8.3** + +- 新增:上传页面重新设计,将主要功能放在了浮动按钮上,主页面用来显示上传列表,避免上传照片比较多时,一直卡在没有进度提示的loading窗口,单张拍照和续上传两个功能仍沿用旧的上传方式。 +- 新增:用户登录页面重新设计,现在分为注册/登录和已登录两个页面,同时已登录页面显示用户信息和全部图床配置信息,并可以拉取云端配置和注销登录。 +- 新增:相册页面现在在切换页面的时候,会保留当前的页面状态,包括页数,选中状态等,同时上传了新图片或者清空了相册数据库后会自动触发相册刷新。 +- 新增:系统状态栏颜色调整为透明色,同时优化了部分页面APPBar的显示效果。 +- 优化:腾讯云COS二级页面的文件底部弹出栏,显示文件名时不会再显示目录前缀了。 +- 优化:部分文本显示现在可以被复制。 +- 修复:图床存储路径为一串空白字符时会导致上传错误的问题。 +- 修复:Github图床相册预览无法显示照片的问题和复制的url无法直接显示的问题。 + +## 2022-10-27 **V1.8.2** + +- 新增:上传页面默的认图床切换列表现在会使用不同的颜色来区分当前的默认图床。 +- 优化:弹出框统一为Cupertino样式。 +- 优化: 网络图片预览加入了加载中和加载失败的状态管理。 +- 优化:重命名了大部分代码文件和部分变量名,使其符合dart命名规范。 +- 优化:解决了绝大部分代码格式不规范问题,共计约450处。 +- 优化:优化了代码结构,提取出了一些公共方法,精简了代码量,共减少约2000行代码。 +- 修复:清空相册数据库页面,弹出框点击确认后,弹出框不会消失的问题。 + +## 2022-10-25 **V1.8.1** + +- 新增:图床仓库管理功能增加了对**SM.MS**的支持。 +- 新增:图床管理主页卡片现在可以拖动排序了,拖动后会自动保存顺序。 +- 优化: 文件全部删除后显示空目录提示背景,而不是空白。 +- 优化: 调整了部分界面里浮动按钮的位置和大小。 +- 修复:网络不佳的情况下,提前退出页面后,请求回调setState报错。 +- 修复: 如果在本地文件浏览目录里删除了已下载的文件,返回下载页面后删除下载任务会报错的问题。 +- 修复: 本地下载文件目录浏览页面里,右滑删除按钮点击无效的问题。 +- 修复: 图床文件浏览页面中,删除文件的时候,选中列表对应元素没有被同步移除的bug。 + +## 2022-10-24 **V1.8.0** + +- 增加了新的功能**图床仓库管理**,目前实现了对**腾讯云COS**的支持(*相当于内置了一个精简版的腾讯COSBrowser*),后续会陆续增加对其他图床的支持,主要能有: + 1. 查看存储桶列表,支持创建和删除,修改访问权限,设置存储桶为腾讯云COS的默认存储桶。 + 2. 查看存储桶中的目录和文件列表,目录和文件都支持右滑删除,文件额外支持右滑分享连接。可新建文件夹,可设置目录为图床仓库的默认目录,文件可复制多格式的链接,重命名,照片可以预览。 + 3. 存储桶管理页面和本地下载目录浏览页面均可按修改时间,文件名,文件大小,文件类型等排序。 + 4. 支持多选上传文件和照片,上传剪贴板内的网络链接。 + 5. 可批量下载和管理已下载文件,支持暂停和继续下载,支持删除已下载文件。 + 6. 支持管理本地下载目录,可重命名和删除已下载文件,支持预览照片和调用其它应用打开文件。 +- 修改了本地相册数据库的存储路径。 +- 从剪贴板链接获取网络图片时,加入了对空文本的处理。 +- 修改了部分字体,部分弹出框修改为ios样式。 +- UI细节优化。 +- 修复了图床api在请求失败时的错误处理。 + +## 2022-10-17 **V1.7.6** + +- 重写了路由管理,优化了路由跳转的体验,修改了跳转动画。 +- 主页/相册/设置页面现在不会左上角出现返回按钮,更加美观。 +- 优化了主页在深色主题下的显示效果。 + +## 2022-10-15 **V1.7.5** + +- 增加了对阿里云OSS的支持。 +- 增加了对又拍云存储的支持。 +- 增加了导出图床配置到剪贴板的功能,导出格式为json。 +- 增加了从剪贴板中的图片链接直接获取图片的功能,并且可以通过换行符分隔多个图片链接来一次性获取多张图片。 +- 增加了上传的时候自定义文件名的功能,使用 `{Y}`、`{y}`、`{m}`、`{d}`、`{uuid}`、`{md5}`等占位符,可选年月日,uuid,md5,随机字符串等任意组合来义文件名。 +- 增加了手动清除缓存的功能。 + +## 2022-10-14 **V1.7.0** + +- 增加了对腾讯云COS的支持。 +- 修改自定义链接格式的占位符为 `$fileName`和 `$url`,来和PicGo保持一致,同时修改了默认的自定义链接格式。 +- 修复了七牛云不设置存储路径的时候保存路径错误的问题。 +- 修复了github,imgur和七牛云导入二维码配置的时候可选参数默认值错误和七牛云数据库保存的数据错误的问题。 + +## 2022-10-13 **V1.6.5** + +- 增加了对七牛云的支持。 +- 调整了配置和图片上传/删除的时候的响应和连接超时时间设置数值。 +- 相册图片的外框默认透明色,选中的时候会有一个红色的边框提示。 + +## 2022-10-12 **V1.6.0** + +- 增加了对Imgur图床的支持,但是由于Imgur的限制,使用的时候需要配置手机代理,在个人手机上配合clash测试可用。 +- 加入了设置配置和图片上传/删除的时候的响应和连接超时时间设置,防止网络不好的情况下卡死。 +- 区分了相册显示的时候的图片地址和复制的时候的图片地址,改善相册加载图片的速度,例如兰空图床在相册小图中显示的是缩略图,预览大图的时候才会加载原图。 +- 修复了设置页面跳转到主页的时候,有时会先跳转到相册页面的问题。 +- 修复了注册用户的时候,同步创建本地相册数据库的代码没有执行的bug。 +- 修复了连续上传功能中,复制的链接的格式错误的bug。 +- 更改了登录页面UI,方便区分出是否已经登录。 + +## 2022-10-11 **V1.5.5** + +- 增加了扫码导入PicGo配置的功能,和PicGo进一步兼容。 +- 增加了对github图床的支持,在主页增加了切换默认上传图床的浮动按钮。 +- 增加了自定义复制链接的格式的功能,和PicGo的自定义格式一样,使用\${url}和\${fileName}来表示链接和文件名,可以在设置中自定义。 +- 增加了新的设置选项,可以选择在删除图片的时候是否同步删除网络端的图片(默认不删除)。 +- 重新整理了源代码文件架构,使得代码更加清晰,方便后续的更新和维护。 +- 修复了相册上翻页功能没有按预期作用的问题。 +- 修复了图片多选的时候,删除功能的bug,同时优化了动画显示,现在不会傻傻的等待了。 +- 修复了相册中显示的图床和默认上传图床不一致的时候无法删除网络端图片的bug。 +- 修复了选中状态会在翻页的时候保留的bug。 +- 修复了设置默认上传图床参数的时候,没有同步更改云端数据库记录的问题。 +- 优化了界面UI,修复了一些组件尺寸的问题。 + +## 2022-10-09 **V1.5.0** + +- 增加了**相册**功能,进一步对标了PicGo,现在PicHoro不仅是一款上传工具,也是一款图床管理工具。 +- 相册模块中实现了这些功能: + 1. 显示已经上传的图片,分页显示,每页12张,可以上划和下拉翻页。 + 2. 分图床显示,可选择显示某个图床的图片。 + 3. 实现了删除图片的功能,删除后会自动刷新相册,默认删除数据库记录和图床上的图片,可选择是否同步删除本地图片。 + 4. 实现了多选功能,可选择多张图片进行删除和复制指定格式的链接。 + 5. 可点击图片查看大图,双击图片可复制指定格式的链接,长按图片可弹出菜单,选择链接格式或者删除图片。 + 6. 相册中的图片数据保存在本地,通过APP内升级时不会丢失。 +- 增加了选择默认图床的页面,可以更直观的知道当前默认图床是哪个。 +- 修复了一些已知的bug。 + +## 2022-10-07 **V1.4.1** + +- 增加了对SM.MS图床的支持。 +- 修复了markdown链接的文件名错误的问题。 + +## 2022-10-07 **V1.4.0** + +- 增加了文件上传自动重命名的功能。 +- 增加了文件上传后自动复制链接的功能,同时可选url,html,markdown,bbcode和带链接的markdown等格式。 +- 增加了软件APP内自动更新的功能。 +- Github国内打开太慢,把项目地址页面换成更新日志页面。 +- 部分bug修复 + +## 2022-10-06 **V1.3.1** + +- 修复了已注册用户在新设备第一次登录的时候,无法正常登录的bug。 +- 修复了连续上传功能在退出的时候会卡在上传中的bug。 +- 已登录的设备在获取云端配置的时候不需要重新输入用户名和密码了。 +- 修复了部分代码小bug + +## 2002-10-05 **V1.3.0** + +- 重构了整个APP的代码架构,把所有的页面和功能性函数都放在了对应文件夹里,方便后续的维护和扩展,同时抽象了上传等功能的接口,后续增加图床时可以直接用。 +- 增加了用户登录和拉取云端配置的功能,可以通过用户名和密码登录,将图床配置等保存在服务器上,这里现在用户本地用3DES加密然后再保存到数据库,除了用户和图床名是明文外,其他的都是密文,这样可以保证用户的隐私。 +- 增加了软件主题切换的功能,增加了软件更新页面。 +- 新设计了软件的图标和启动画面,同时对软件的UI进行了一些优化。 +- 一些BUG修复 + +## 2002-10-04 **V1.2.1** + +- 增加了上传图片和配置图床时的等待动画。 +- 在设置页面增加了底部导航栏,修改了部分按钮的名字。 +- 调整了部分弹出式提示框的实现方式,修改为自动消失的小提示框,同时部分重要提示框禁止了点击背景消失。 +- 修复了项目地址页面打不开的问题。 +- 优化了部分代码。 + +## 2002-10-03 **V1.2.0** + +- 现在从相册里选择照片的时候可以多选了,上传功能也更新为批量上传。 +- 增加了新的设置页面,可以在设置页面里选择图床配制,项目地址和联系作者。 +- 重构了部分代码,为后续增加图床平台做准备。 +- 优化了页面布局,改变了部分UI。 + +## 2002-10-03 **V1.1.0** + +- 增加了对权限的主动获取,避免用户手动授予权限。 +- 增加了对拍照后自动上传并返回拍照界面的功能。 +- 增加了对各种异常的提醒。 +- 优化了弹出框的显示布局。 +- 优化了页面布局。 + +## 2022-10-02 **V1.0.0** + +- 项目初始化,完成基本的上传功能,目前仅支持兰空图床,需要手动授予存储和相机权限。 diff --git a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index 4c648e5..f8f2103 100644 --- a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -17,122 +17,127 @@ public final class GeneratedPluginRegistrant { public static void registerWith(@NonNull FlutterEngine flutterEngine) { try { flutterEngine.getPlugins().add(new de.bytepark.autoorientation.AutoOrientationPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin auto_orientation, de.bytepark.autoorientation.AutoOrientationPlugin", e); } try { flutterEngine.getPlugins().add(new de.mintware.barcode_scan.BarcodeScanPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin barcode_scan2, de.mintware.barcode_scan.BarcodeScanPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.camera.CameraPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin camera_android, io.flutter.plugins.camera.CameraPlugin", e); } + try { + flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.device_info.DeviceInfoPlusPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin device_info_plus, dev.fluttercommunity.plus.device_info.DeviceInfoPlusPlugin", e); + } try { flutterEngine.getPlugins().add(new com.pinciat.external_path.ExternalPathPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin external_path, com.pinciat.external_path.ExternalPathPlugin", e); } try { flutterEngine.getPlugins().add(new com.mr.flutter.plugin.filepicker.FilePickerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin file_picker, com.mr.flutter.plugin.filepicker.FilePickerPlugin", e); } try { flutterEngine.getPlugins().add(new com.fluttercandies.flutter_image_compress.ImageCompressPlugin()); - } catch(Exception e) { - Log.e(TAG, "Error registering plugin flutter_image_compress, com.fluttercandies.flutter_image_compress.ImageCompressPlugin", e); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin flutter_image_compress_common, com.fluttercandies.flutter_image_compress.ImageCompressPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin flutter_plugin_android_lifecycle, io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin", e); } try { flutterEngine.getPlugins().add(new software.solid.fluttervlcplayer.FlutterVlcPlayerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin flutter_vlc_player, software.solid.fluttervlcplayer.FlutterVlcPlayerPlugin", e); } try { flutterEngine.getPlugins().add(new io.github.ponnamkarthik.toast.fluttertoast.FlutterToastPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin fluttertoast, io.github.ponnamkarthik.toast.fluttertoast.FlutterToastPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.imagepicker.ImagePickerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin image_picker_android, io.flutter.plugins.imagepicker.ImagePickerPlugin", e); } try { flutterEngine.getPlugins().add(new com.crazecoder.openfile.OpenFilePlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin open_filex, com.crazecoder.openfile.OpenFilePlugin", e); } try { flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin package_info_plus, dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e); } try { flutterEngine.getPlugins().add(new com.baseflow.permissionhandler.PermissionHandlerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin permission_handler_android, com.baseflow.permissionhandler.PermissionHandlerPlugin", e); } try { flutterEngine.getPlugins().add(new com.fluttercandies.photo_manager.PhotoManagerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin photo_manager, com.fluttercandies.photo_manager.PhotoManagerPlugin", e); } try { flutterEngine.getPlugins().add(new com.example.r_upgrade.RUpgradePlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin r_upgrade, com.example.r_upgrade.RUpgradePlugin", e); } try { flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.share.SharePlusPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin share_plus, dev.fluttercommunity.plus.share.SharePlusPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin shared_preferences_android, io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin", e); } try { flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin sqflite, com.tekartik.sqflite.SqflitePlugin", e); } try { flutterEngine.getPlugins().add(new com.syncfusion.flutter.pdfviewer.SyncfusionFlutterPdfViewerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin syncfusion_flutter_pdfviewer, com.syncfusion.flutter.pdfviewer.SyncfusionFlutterPdfViewerPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin url_launcher_android, io.flutter.plugins.urllauncher.UrlLauncherPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.videoplayer.VideoPlayerPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin video_player_android, io.flutter.plugins.videoplayer.VideoPlayerPlugin", e); } try { - flutterEngine.getPlugins().add(new creativemaybeno.wakelock.WakelockPlugin()); - } catch(Exception e) { - Log.e(TAG, "Error registering plugin wakelock, creativemaybeno.wakelock.WakelockPlugin", e); + flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.wakelock.WakelockPlusPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin wakelock_plus, dev.fluttercommunity.plus.wakelock.WakelockPlusPlugin", e); } try { flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); - } catch(Exception e) { + } catch (Exception e) { Log.e(TAG, "Error registering plugin webview_flutter_android, io.flutter.plugins.webviewflutter.WebViewFlutterPlugin", e); } } diff --git a/assets/files/UpdateLog.md b/assets/files/UpdateLog.md index f349b91..5c9cc0f 100644 --- a/assets/files/UpdateLog.md +++ b/assets/files/UpdateLog.md @@ -1,195 +1,212 @@ -# 文件预览支持 - -| 平台 | 图片 | PDF | 文本文件 | 视频 | -| ------ | :--: | :-: | :------: | :--: | -| Alist | ✅ | ✅ | ✅ | ✅ | -| 阿里云 | ✅ | ✅ | ✅ | ✅ | -| S3 | ✅ | ✅ | ✅ | ✅ | -| 腾讯云 | ✅ | ✅ | ✅ | ✅ | -| 又拍云 | ✅ | ✅ | ✅ | ✅ | -| 七牛云 | ✅ | ✅ | ✅ | ✅ | -| WebDAV | ✅ | ✅ | ✅ | ✅* | -| FTP | ✅ | ❌ | ✅ | ❌ | -| Github | ✅ | ❌ | ✅ | ❌ | -| Imgur | ✅ | ❌ | ❌ | ✅ | -| 兰空 | ✅ | ❌ | ❌ | ❌ | -| SM.MS | ✅ | ❌ | ❌ | ❌ | - -注:由于vlc播放器的限制,WebDAV的视频预览只能播放部分格式的视频。 - -- 2023-07-25: **V2.1.2**: - - - 修复:修复了imgur仓库无法进入的问题 - -- 2023-07-10: **V2.1.1**: - - - 更新:更新了alist驱动列表,与最新版(3.21.0)保持同步 - -- 2023-06-20: **V2.1.0**: - - - webdav现在支持设置自定义域名 - - ftp现在支持设置自定义域名 - -- 2023-05-04: **V2.0.0**: - - - 移除了用户登录和云端同步系统,现在所有数据保存于用户本地 - - 移除了imgur管理登录页面对clientsecret的需求 - - 更新了alist驱动列表,与最新版(3.16.3)保持同步 - - 修复了重复设置Alist为默认图床时,默认相册设置错误的问题 - -- 2023-04-14: **V1.11.0**: - - - 维护:更换了github加速代理。 - - 修复:修复了复制链接时如果链接中有逗号会导致格式错误的问题。 - - 修复:修复了alist复制链接格式错误的问题。 - -- 2023-02-28: **V1.10.0**: - - - 新增:s3/阿里云/腾讯云等平台现在可以单独为存储桶设置自定义域名了。 - - 新增:现在会在安装或者启动时获取安装未知应用权限,避免APP无法启动。 - - 维护:部分代码精简 - - 修复:修复了s3平台文件地址错误的问题。 - - 修复:修复了图片缓存导致相同地址的图片无法更新的问题。 - -- 2023-01-05: **V1.9.9**: - - - 新增:更换了版本升级使用的下载地址为cdn加速地址,优化了下载速度。 - - 新增:将作者QQ二维码修改为软件反馈交流群二维码。 - - 维护:精简了部分通用代码,方便后续维护和功能扩展,共减少约8000行冗余代码。 - - 修复:修复了设置主题为自动后,APP无法正常启动的问题。 - - 修复:修复了在上传下载页面删除上传任务后,使用 `全部开始`按钮仍然会上传已删除的任务的问题。 - - 修复:修复了在进入webdav上传下载页面时,自动切换标签页没有正常作用的问题。 -- 2022-12-18: **V1.9.8**: - - - 新增:添加了图片压缩功能,现在可以选择在上传图片前先进行压缩了,可选压缩后格式为jpg、png和webp,并且可以自定义最小宽度、最小高度和压缩后质量。 - - 新增:S3 API兼容平台上传时现在会主动修改content-type。 - - 优化:添加了webp文件格式的图标。 -- 2022-12-05: **V1.9.7**: - - - 新增:添加了对**WebDAV**的支持,使用坚果云webdav和Alist V3的webdav测试通过。 - - 新增:Alist V3现在可以不登录访问了,只需要设置Alist域名,即可在管理页面中查看文件。 - - 修复:修复了清空相册数据库页面没有显示阿里云的问题。 -- 2022-12-02: **V1.9.6**: - - - 新增:新增了对[Alist V3](https://alist.nn.ci/zh/)的支持,现在可以通过Alist间接管理其支持的各种存储和网盘。支持的平台包括:本地存储,阿里云盘,百度网盘,夸克网盘,蓝奏云,谷歌相册,115,OneDrive,天翼云盘,GoogleDrive,123云盘,FTP / SFTP,PikPak,S3,又拍云,WebDav,Teambition,分秒帧,移动云盘等等。 - - Alist的token有有效期,PicHoro会每天尝试更新一次token,如果遇到错误,请尝试重新设置Alist来更新token。 - - 支持通过Alist来查看图片和观看视频。 - - 支持快捷操作Alist存储,包括新增,卸载,修改配置,启用,禁用等。 - - 支持上传/下载,新建文件夹/重命名/删除/分享链接等各种操作,使用百度网盘官方接口时,会自动添加 `User-Agent:pan.baidu.com`的请求头。 - - 由于Alist支持的平台比较多,个人一些平台没有账号,未能全部完成测试,如果发现有问题,请提交issue。已经测试过的平台有: - - Alist V3 - - 阿里云盘 - - 百度网盘 - - 本机存储 - - SFTP - - FTP - - 一刻相册 - - WebDav(使用坚果云webdav测试) - - 新增:增加了对**PDF**格式文件在线预览的支持,可以搜索和跳转页面。 - - 新增:增加了对**音视频文件**在线播放的支持,其中: - - mp4, flv,m4v,mp3,avi,mpg,flac,ogg,ts,aac,m4a,vob等格式支持播放列表功能,列表由当前目录下的视频文件组成。 - - mkv和rmvb等格式由于后台实现的问题,暂时不支持播放列表功能。 - - mkv文件可以自动识别和加载字幕文件,字幕文件需要与视频的文件名相同,后缀名为srt/ass/vtt/sbv/ttml/dfxp/ssa之一。 - - **为了解码mkv等格式,添加了VLC依赖,包体积增加较多,我在尝试缩减和考虑是否提供不带VLC的版本。** - - 新增:现在支持**更多的文本文件格式**预览,涵盖了各种常见编程语言源文件。 - - 新增:图床管理页面增加了重置排序按钮,可以将图床的排序重置为默认排序。 - - 优化:压缩了部分assets文件,尽量减少对包体积的影响,同时删除了部分未使用的assets文件。 - - 优化:更换了网页浏览的实现方式。 - - 优化:用户登录后的图床信息查看页,现在按照图床名称排序。 - - 优化:优化部分UI尺寸。 - - 优化:优化了部分冗余代码。 - - 修复:修复了图片预览时会在最后额外添加一页空白页的问题 - - 修复:修复了注销登录时没有清空S3上传/下载列表的问题。 - - 修复:修复了连续上传模式下,使用S3上传时相册显示不正确的问题。 - - 修复:修复了图床设置为S3时,相册内删除会错误的将url当做本地文件名进行删除的问题。 - - 修复:修复了使用S3时,返回的图片URL不包括存储桶,导致图片无法显示的问题。 - - 修复:修复了图床管理页面的排序问题。 -- 2022-11-25: **V1.9.5**: - - - 新增:设置界面现在在有新版本时会显示新版本号,且 `检查更新`会变为 `有新版本`来提示用户。 - - 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 - - 优化:调整了上传/下载界面和主页上传框任务列表的顺序,现在后添加的任务会显示在最上面。 - - 优化:调整了上传界面和主页上传框任务列表的UI,进度条修改到任务名称的右侧,减少单个任务的宽度。 - - 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 - - 优化:修改了部分界面UI。 - - 修复:修复了在上传界面和主页上传框内,点击全部上传时,已上传完成的任务会被重新上传的问题。 - - 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 - - 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 -- 2022-11-24: **V1.9.4 beta 1**: - - - 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 -- 2022-11-24: **V1.9.4 beta 2**: - - - 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 - - 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 - - 优化:修改了部分界面UI。 - - 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 -- 2022-11-24: **V1.9.4 beta 1**: - - - 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 -- 2022-11-22: **V1.9.3**: - - - 新增:现在进入联系作者界面时会自动复制作者QQ号到剪贴板。 - - 优化:修改了连续上传模式的逻辑,由 `拍照->校验->上传->拍照`改为 `拍照->校验(异步上传)->拍照`,将上传操作修改为异步执行,提高用户体验。 - - 优化:修改了部分提示信息。 - - 优化:解决了上传界面和相册界面的右上角设置弹出栏,必须点击文字部分才能弹出窗口的问题。 - - 优化:优化了APP的启动速度。 - - 修复:修复了aws上传和下载列表会在APP关闭后被清空的问题。 - - 修复:修复了批量复制链接时,从第二行开始,开头会多一个空格的问题。 -- 2022-11-21: **V1.9.2**: - - - 优化:更换了github和七牛云的图标,优化在深色模式下的显示效果。 - - 修复:修复了登录时没有同步FTP和S3平台配置的问题。 - - 修复:修复了注销登录后会导致图床管理页面无法正常显示的问题。 -- 2022-11-19: **V1.9.1**: - - - 新增:增加了对**兼容S3 API协议的平台**的支持,目前已测试通过的平台包括AWS S3,backblaze和cloudfare R2,其他平台只要兼容S3 API协议也可使用,但个人未全部测试,如果你使用了其他平台,欢迎反馈测试结果。 - - 新增:现在每种图床可以保存最多26组备用配置,可一键替换默认图床配置,方便在不同的图床目录或例如在不同的S3兼容平台之间切换。 - - 备用配置保存于本地,不会上传到云端。 - - 可将备用配置导出为json格式并复制到剪贴板内,并可将剪贴板内的json格式的备用配置导入到APP内,方便保存和同步。 - - 修改配置时可导入现有默认配置,方便快速修改。 - - 优化:检查图床配置时现在会显示loading界面,避免误认为无响应。 - - 修复:修复了腾讯云、阿里云、七牛云和又拍云在获取文件列表时,单目录下获取文件数量存在上限,导致不能获得全部文件列表的问题。 - - 修复:修复了又拍云最多只能获取到25个存储桶的问题。 - - 修复:修复了每次进入目录时都会检查一次存储桶是否为空的问题,大幅提高了目录文件的加载速度。 - - 修复:修复了ftp管理起始目录如果不以'/'结尾导致报错的问题。 - - 修复:修复了smms数据库更新数据的SQL语句错误导致无法更新图床配置的问题。 -- 2022-11-16: **V1.9.0**: - - - 新增:新增了对**Imgur**管理功能的支持。 - - 图片查看时使用CDN进行了加速,避免图片加载过慢或失败。 - - 新增:新增了对**FTP**和**SSH/SFTP**上传、相册、管理功能的支持,可匿名或SSH登录服务器管理文件,另外作为补充,内置了**SSH终端**,可直接管理**云服务器**。 - - 由于一般情况下普通FTP功能限制较多,能做到的操作不多,因此管理功能仅支持SFTP方式。 - - 禁止了对文件名包含 `*#?` 等特殊字符的文件的删除操作,防止出现意外情况。 - - 由于服务器操作的敏感性,APP内仅实现了一些安全操作,其它操作请使用内置**SSH终端**进行。 - - 为了解决FTP文件上图片无法直接预览的问题,在上传时会自动生成一份图片缩略图保存于缓存目录下用于相册内查看,在从相册中删除图片时会自动删除缓存目录下的缩略图。 - - 缩略图保存路径为 `手机存储/Android/data/com.example.horopic/cache/ftp/`,可根据需要自行删除。 - - 管理功能针对普通目录和文件,为避免混乱,不支持软链接等特殊类型文件,如需管理软链接等特殊类型文件,请使用内置**SSH终端**。 - - 支持选择管理功能入口目录,方便快速进入指定目录。 - - 新增:Github和SSH/SFTP管理页面现在支持预览markdown文件。 - - 新增:图床配置导出增加了**导出全部**的选项。 - - 新增:修改了图片上传和删除时保存配置信息的逻辑,现在会在本地数据库保存每一张图片上传时对应的图床配置,这样即使上传后修改了图床配置,仍然可以正常删除云端图片,同时为后续的一个图床多套配置功能做准备。 - - 新增:上传页面和相册页面顶部栏增加了配置按钮,方便快速修改常用配置。 - - 优化:现在浏览本地文件时如果目标已被删除会显示空白提示页面。 - - 优化:一些UI优化,包括如下: - - 本地图片预览修改为页面居中显示。 - - 首页和相册页面的图床选择弹出栏的的顺序调整为图床首字母字典序。 - - 图床配置和默认图床选择页面的顺序调整为图床首字母字典序。 - - 更新日志界面现在支持跳转链接和选择文字。 - - 修复:修复了在配置页面修改默认图床和单独图床配置页面内设置为默认图床后,上传页面弹出栏显示的默认图床和相册页面显示的图床没有同步更新的问题。 - - 修复:修复了兰空图床管理页面内删除相册后,后续的部分文件会被错误显示为文件夹的问题。 -- 2022-11-12: **V1.8.9**: - - - 新增:新增了对**Github**管理功能的支持,并且可以浏览其它用户的公开仓库,同时可以下载其它用户的公开仓库的文件,此外复制链接时对私有仓库还会添加临时访问token,以便于下载私有仓库的文件。 - - 新增:使用github图床时,如果未设置自定义域名,现在相册预览,文件下载等情况下会默认使用加速服务,以解决国内可能无法访问raw.githubusercontent.com,导致图片无法显示或者下载失败的问题。 - - 新增:新增了近200个文件图标,使得文件管理界面更加美观。 - - 修复:将上传界面的同时可进行任务数修改为1,以解决Github同时上传冲突的上传失败问题。 -- 2022-11-09: **V1.8.8**: - - - 新增:**由于新增了字段,旧版本APP保存兰空图床配置会失败,请尽快更新到最新版本** - - 新增:新建了软件的介绍和配置说明网站[https://pichoro.horosama.com](https://pichoro.horosama.com),并在软件配置主页加入了 `软件主页`跳转选项 - - 新增:兰空图床显示了当前token,同时在已有token的情况下,可以直接获取策略ID和相册ID列表,不再需要输入用户名和密码。 - - 新增:兰空图床配置参数增加了相册ID,管理界面上传时也会上传到对应相册,但限于以下两种情况下才会生效: +# 更新日志 + +## 2023-09-17 **V2.2.2** + +- 新增:又拍云现在支持设置防盗链token和过期时间参数 +- 新增:现在首页标题会显示当前的默认上传图床 +- 新增:时间戳重命名现在不再额外添加5个随机字符 +- 新增:自定义重命名中的时间戳选项现在采用毫秒,而不是秒 +- 新增:移除了二维码导入配置时的校验步骤以提高导入速度 +- 新增:现在保存图床设置时不再强制进行校验(与`校验当前配置`功能重复) +- 新增:现在拷贝链接时,会对文件名进行basename处理 +- 新增:现在查看配置时密码不再加密显示,方便配置 +- 修复:修复了通过拍照上传时,文件名中含有的二级路径没有生效的问题 +- 修复:修复了文件名中含有二级路径时,开启图片压缩后无法上传的问题 +- 修复:imgur移除了失效的CDN加速网址 +- 修复:修复了文件名中含有二级路径时,部分图床无法远程删除图片的问题 +- 修复:修复了从二维码导入时,路径设置为根目录时导入错误的问题 +- 维护:重构了大部分代码以提高性能和可维护性 + +## 2023-08-26 **V2.1.3** + +- 依赖:从Flutter 3.3版本迁移至 3.13 版本 +- 优化:移除了底部栏的文字标签 +- 更新:阿里云存储桶区域现在支持无地域属性 +- 更新:腾讯云移除了不再支持的莫斯科区域 +- 更新:FTP现在删除功能会额外校验路径,避免误删除 + +## 2023-07-25 **V2.1.2** + +- 修复:修复了imgur仓库无法进入的问题 + +## 2023-06-20 **V2.1.1** + +- 更新:更新了alist驱动列表,与最新版(3.21.0)保持同步 + +## 2023-06-20 **V2.1.0** + +- webdav现在支持设置自定义域名 +- ftp现在支持设置自定义域名 + +## 2023-05-04 **V2.0.0** + +- 移除了用户登录和云端同步系统,现在所有数据保存于用户本地 +- 移除了imgur管理登录页面对clientsecret的需求 +- 更新了alist驱动列表,与最新版(3.16.3)保持同步 +- 修复了重复设置Alist为默认图床时,默认相册设置错误的问题 + +## 2023-04-14 **V1.11.0** + +- 维护:更换了github加速代理。 +- 修复:修复了复制链接时如果链接中有逗号会导致格式错误的问题。 +- 修复:修复了alist复制链接格式错误的问题。 + +## 2023-02-28 **V1.10.0** + +- 新增:s3/阿里云/腾讯云等平台现在可以单独为存储桶设置自定义域名了。 +- 新增:现在会在安装或者启动时获取安装未知应用权限,避免APP无法启动。 +- 维护:部分代码精简 +- 修复:修复了s3平台文件地址错误的问题。 +- 修复:修复了图片缓存导致相同地址的图片无法更新的问题。 + +## 2023-01-05 **V1.9.9** + +- 新增:更换了版本升级使用的下载地址为cdn加速地址,优化了下载速度。 +- 新增:将作者QQ二维码修改为软件反馈交流群二维码。 +- 维护:精简了部分通用代码,方便后续维护和功能扩展,共减少约8000行冗余代码。 +- 修复:修复了设置主题为自动后,APP无法正常启动的问题。 +- 修复:修复了在上传下载页面删除上传任务后,使用 `全部开始`按钮仍然会上传已删除的任务的问题。 +- 修复:修复了在进入webdav上传下载页面时,自动切换标签页没有正常作用的问题。 + +## 2022-12-18 **V1.9.8** + +- 新增:添加了图片压缩功能,现在可以选择在上传图片前先进行压缩了,可选压缩后格式为jpg、png和webp,并且可以自定义最小宽度、最小高度和压缩后质量。 +- 新增:S3 API兼容平台上传时现在会主动修改content-type。 +- 优化:添加了webp文件格式的图标。 + +## 2022-12-05 **V1.9.7** + +- 新增:添加了对**WebDAV**的支持,使用坚果云webdav和Alist V3的webdav测试通过。 +- 新增:Alist V3现在可以不登录访问了,只需要设置Alist域名,即可在管理页面中查看文件。 +- 修复:修复了清空相册数据库页面没有显示阿里云的问题。 + +## 2022-12-02 **V1.9.6** + +- 新增:新增了对[Alist V3](https://alist.nn.ci/zh/)的支持,现在可以通过Alist间接管理其支持的各种存储和网盘。支持的平台包括:本地存储,阿里云盘,百度网盘,夸克网盘,蓝奏云,谷歌相册,115,OneDrive,天翼云盘,GoogleDrive,123云盘,FTP / SFTP,PikPak,S3,又拍云,WebDav,Teambition,分秒帧,移动云盘等等。 + - Alist的token有有效期,PicHoro会每天尝试更新一次token,如果遇到错误,请尝试重新设置Alist来更新token。 + - 支持通过Alist来查看图片和观看视频。 + - 支持快捷操作Alist存储,包括新增,卸载,修改配置,启用,禁用等。 + - 支持上传/下载,新建文件夹/重命名/删除/分享链接等各种操作,使用百度网盘官方接口时,会自动添加 `User-Agent:pan.baidu.com`的请求头。 + - 由于Alist支持的平台比较多,个人一些平台没有账号,未能全部完成测试,如果发现有问题,请提交issue。已经测试过的平台有: + - Alist V3 + - 阿里云盘 + - 百度网盘 + - 本机存储 + - SFTP + - FTP + - 一刻相册 + - WebDav(使用坚果云webdav测试) +- 新增:增加了对**PDF**格式文件在线预览的支持,可以搜索和跳转页面。 +- 新增:增加了对**音视频文件**在线播放的支持,其中: + - mp4, flv,m4v,mp3,avi,mpg,flac,ogg,ts,aac,m4a,vob等格式支持播放列表功能,列表由当前目录下的视频文件组成。 + - mkv和rmvb等格式由于后台实现的问题,暂时不支持播放列表功能。 + - mkv文件可以自动识别和加载字幕文件,字幕文件需要与视频的文件名相同,后缀名为srt/ass/vtt/sbv/ttml/dfxp/ssa之一。 + - **为了解码mkv等格式,添加了VLC依赖,包体积增加较多,我在尝试缩减和考虑是否提供不带VLC的版本。** +- 新增:现在支持**更多的文本文件格式**预览,涵盖了各种常见编程语言源文件。 +- 新增:图床管理页面增加了重置排序按钮,可以将图床的排序重置为默认排序。 +- 优化:压缩了部分assets文件,尽量减少对包体积的影响,同时删除了部分未使用的assets文件。 +- 优化:更换了网页浏览的实现方式。 +- 优化:用户登录后的图床信息查看页,现在按照图床名称排序。 +- 优化:优化部分UI尺寸。 +- 优化:优化了部分冗余代码。 +- 修复:修复了图片预览时会在最后额外添加一页空白页的问题 +- 修复:修复了注销登录时没有清空S3上传/下载列表的问题。 +- 修复:修复了连续上传模式下,使用S3上传时相册显示不正确的问题。 +- 修复:修复了图床设置为S3时,相册内删除会错误的将url当做本地文件名进行删除的问题。 +- 修复:修复了使用S3时,返回的图片URL不包括存储桶,导致图片无法显示的问题。 +- 修复:修复了图床管理页面的排序问题。 + +## 2022-11-25 **V1.9.5** + +- 新增:设置界面现在在有新版本时会显示新版本号,且 `检查更新`会变为 `有新版本`来提示用户。 +- 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 +- 优化:调整了上传/下载界面和主页上传框任务列表的顺序,现在后添加的任务会显示在最上面。 +- 优化:调整了上传界面和主页上传框任务列表的UI,进度条修改到任务名称的右侧,减少单个任务的宽度。 +- 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 +- 优化:修改了部分界面UI。 +- 修复:修复了在上传界面和主页上传框内,点击全部上传时,已上传完成的任务会被重新上传的问题。 +- 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 +- 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 + +## 2022-11-24 **V1.9.4 beta 2** + +- 新增:新增绿色、紫色、橙色、粉色、青色和金色主题。 +- 优化:修改了部分图标和字体的颜色,优化其在暗色模式下的显示效果。 +- 优化:修改了部分界面UI。 +- 修复:修复了又拍云图床后缀为空时,会在复制的链接尾部添加一个空格的问题。 + +## 2022-11-24 **V1.9.4 beta 1** + +- 修复:修复了图床设置时,如果参数内包含中文,会导致报错的问题。 + +## 2022-11-22 **V1.9.3** + +- 新增:现在进入联系作者界面时会自动复制作者QQ号到剪贴板。 +- 优化:修改了连续上传模式的逻辑,由 `拍照->校验->上传->拍照`改为 `拍照->校验(异步上传)->拍照`,将上传操作修改为异步执行,提高用户体验。 +- 优化:修改了部分提示信息。 +- 优化:解决了上传界面和相册界面的右上角设置弹出栏,必须点击文字部分才能弹出窗口的问题。 +- 优化:优化了APP的启动速度。 +- 修复:修复了aws上传和下载列表会在APP关闭后被清空的问题。 +- 修复:修复了批量复制链接时,从第二行开始,开头会多一个空格的问题。 + +## 2022-11-21 **V1.9.2** + +- 优化:更换了github和七牛云的图标,优化在深色模式下的显示效果。 +- 修复:修复了登录时没有同步FTP和S3平台配置的问题。 +- 修复:修复了注销登录后会导致图床管理页面无法正常显示的问题。 + +## 2022-11-19 **V1.9.1** + +- 新增:增加了对**兼容S3 API协议的平台**的支持,目前已测试通过的平台包括AWS S3,backblaze和cloudfare R2,其他平台只要兼容S3 API协议也可使用,但个未全部测试,如果你使用了其他平台,欢迎反馈测试结果。 +- 新增:现在每种图床可以保存最多26组备用配置,可一键替换默认图床配置,方便在不同的图床目录或例如在不同的S3兼容平台之间切换。 + - 备用配置保存于本地,不会上传到云端。 + - 可将备用配置导出为json格式并复制到剪贴板内,并可将剪贴板内的json格式的备用配置导入到APP内,方便保存和同步。 + - 修改配置时可导入现有默认配置,方便快速修改。 +- 优化:检查图床配置时现在会显示loading界面,避免误认为无响应。 +- 修复:修复了腾讯云、阿里云、七牛云和又拍云在获取文件列表时,单目录下获取文件数量存在上限,导致不能获得全部文件列表的问题。 +- 修复:修复了又拍云最多只能获取到25个存储桶的问题。 +- 修复:修复了每次进入目录时都会检查一次存储桶是否为空的问题,大幅提高了目录文件的加载速度。 +- 修复:修复了ftp管理起始目录如果不以'/'结尾导致报错的问题。 +- 修复:修复了smms数据库更新数据的SQL语句错误导致无法更新图床配置的问题。 + +## 2022-11-16 **V1.9.0** + +- 新增:新增了对**Imgur**管理功能的支持。 + - 图片查看时使用CDN进行了加速,避免图片加载过慢或失败。 +- 新增:新增了对**FTP**和**SSH/SFTP**上传、相册、管理功能的支持,可匿名或SSH登录服务器管理文件,另外作为补充,内置了**SSH终端**,可直接管理**云服务**。 + - 由于一般情况下普通FTP功能限制较多,能做到的操作不多,因此管理功能仅支持SFTP方式。 + - 禁止了对文件名包含 `*#?` 等特殊字符的文件的删除操作,防止出现意外情况。 + - 由于服务器操作的敏感性,APP内仅实现了一些安全操作,其它操作请使用内置**SSH终端**进行。 + - 为了解决FTP文件上图片无法直接预览的问题,在上传时会自动生成一份图片缩略图保存于缓存目录下用于相册内查看,在从相册中删除图片时会自动删除缓存目下的缩略图。 + - 缩略图保存路径为 `手机存储/Android/data/com.example.horopic/cache/ftp/`,可根据需要自行删除。 + - 管理功能针对普通目录和文件,为避免混乱,不支持软链接等特殊类型文件,如需管理软链接等特殊类型文件,请使用内置**SSH终端**。 + - 支持选择管理功能入口目录,方便快速进入指定目录。 +- 新增:Github和SSH/SFTP管理页面现在支持预览markdown文件。 +- 新增:图床配置导出增加了**导出全部**的选项。 +- 新增:修改了图片上传和删除时保存配置信息的逻辑,现在会在本地数据库保存每一张图片上传时对应的图床配置,这样即使上传后修改了图床配置,仍然可以正常除云端图片,同时为后续的一个图床多套配置功能做准备。 +- 新增:上传页面和相册页面顶部栏增加了配置按钮,方便快速修改常用配置。 +- 优化:现在浏览本地文件时如果目标已被删除会显示空白提示页面。 +- 优化:一些UI优化,包括如下: + - 本地图片预览修改为页面居中显示。 + - 首页和相册页面的图床选择弹出栏的的顺序调整为图床首字母字典序。 + - 图床配置和默认图床选择页面的顺序调整为图床首字母字典序。 + - 更新日志界面现在支持跳转链接和选择文字。 +- 修复:修复了在配置页面修改默认图床和单独图床配置页面内设置为默认图床后,上传页面弹出栏显示的默认图床和相册页面显示的图床没有同步更新的问题。 +- 修复:修复了兰空图床管理页面内删除相册后,后续的部分文件会被错误显示为文件夹的问题。 + +## 2022-11-12 **V1.8.9** + +- 新增:新增了对**Github**管理功能的支持,并且可以浏览其它用户的公开仓库,同时可以下载其它用户的公开仓库的文件,此外复制链接时对私有仓库还会添加临访问token,以便于下载私有仓库的文件。 +- 新增:使用github图床时,如果未设置自定义域名,现在相册预览,文件下载等情况下会默认使用加速服务,以解决国内可能无法访问raw.githubusercontent.com导致图片无法显示或者下载失败的问题。 +- 新增:新增了近200个文件图标,使得文件管理界面更加美观。 +- 修复:将上传界面的同时可进行任务数修改为1,以解决Github同时上传冲突的上传失败问题。 + +## 2022-11-09 **V1.8.8** + +- 新增:**由于新增了字段,旧版本APP保存兰空图床配置会失败,请尽快更新到最新版本** +- 新增:新建了软件的介绍和配置说明网站[https://pichoro.horosama.com](https://pichoro.horosama.com),并在软件配置主页加入了 `软件主页`跳转选项 +- 新增:兰空图床显示了当前token,同时在已有token的情况下,可以直接获取策略ID和相册ID列表,不再需要输入用户名和密码。 +- 新增:兰空图床配置参数增加了相册ID,管理界面上传时也会上传到对应相册,但限于以下两种情况下才会生效: 1. 基于付费企业版兰空图床搭建 2. 开源免费版需要自己或者联系管理员修改源代码文件,修改方式为打开 **/app/Services/ImageService.php**文件,修改第139行,原文件为 @@ -215,200 +232,223 @@ } ``` -- - 新增:APP启动时现在会自动清理已下载的新版本安装包,避免占用过多空间。 - - 优化:修改了图床配置界面的图标UI。 - - 修复:修复了兰空图床二维码扫描没有反应的问题,感谢知乎用户@力子头的反馈 -- 2022-11-08: **V1.8.7**: - - - 新增:图床仓库管理功能增加了对**兰空图床**的支持 - - 优化:修改了相册和文件浏览页面缩略图的显示方式,从cover修改为fill。 - - 修复:修复了如果没有先单独下载一次文件,直接全部下载时,无法正常创建下载目录导致下载失败的问题。 -- 2022-11-08: **V1.8.6**: - - - 新增:图床仓库管理功能增加了对**七牛云**的支持 - - 新增:图床管理功能文件浏览新增了查看文件详情页,可查看文件的详细信息。 - - 新增:现在图床设置页面会自动填充已保存的配置信息,方便快速查看和更改配置。 - - 新增:阿里云新建文件夹时,加入了对文件夹名的预检查和处理,自动去除开头和结尾的'/',避免创建失败。 - - 优化:修改了部分UI的表现使其更加美观。 - - 修复:修复了七牛云亚太首尔地区上传api路径错误的问题。 - - 修复:修复了七牛云删除云端文件时,如果路径设置为根目录,会导致删除失败的问题。 - - 修复:修复了阿里云OSS上传文件时,如果路径设置为根目录,会导致上传失败的问题。 - - 修复: 修复了管理功能浏览文件时,网络错误会导致界面卡在loading的问题。 - - 修复:修复了部分界面UI错误,修复了日志界面的一些显示问题。 -- 2022-11-04: **V1.8.5**: - - - 新增:图床仓库管理功能增加了对**又拍云**的支持,需要单独登录一次又拍云的账号密码,实现了服务账号管理、存储桶管理到文件管理的完整支持。 - - 新增:重构了下载页面,现在同时显示上传和下载任务,并且现在重新进入上传/下载页面不会丢失任务了,并且现在可以单独删除任务了。 - - 优化:上传界面内用户未登录时,现在会提示用户登录后才能使用相关功能。 - - 优化:相册内删除图片时,如果云端删除失败,现在会提示用户并中止删除流程。 - - 优化:现在设置又拍云图床时,网站后缀不再是必选参数。 - - 优化:现在部分页面返回后会主动触发上级页面刷新,以保证数据的及时更新。 - - 优化: 目录内文件全部删除后现在正确显示空目录提示背景,而不是空白。 - - 优化:优化了注销登录的处理,现在会将所有用户设置重置为默认值。 - - 优化:修改了部分图标和文字,以及部分页面的布局。 - - 修复:修复了相册页面,来回切换图床会导致部分图床无法正常删除云端图片的问题。 - - 修复:修复了当图片路径中包含中文时,又拍云图床无法正常删除云端图片的问题。 - - 修复:修复了又拍云图床上传图片后,图片链接多了一个'/'的问题。 - - 修复:修复了未登录或者配置图床的时候,对应图床管理页面会卡住的问题。 - - 修复:修复了从剪贴板上传文件功能在部分情况下无法正常使用的问题。 - - 修复:修复了部分日志函数名记录错误的问题。 -- 2022-11-01: **V1.8.4**: - - - 新增:图床仓库管理功能增加了对**阿里云**的支持。 - - 新增:修改了上传文件时重命名的逻辑,现在不会同步重命名本地文件了。**感谢@Yurzi的建议**。 - - 新增:自定义文件重命名现在增加了不合规格式的检查,并且由于重命名逻辑的修改,现在可以使用'/'来同步新建文件夹了。**感谢@Yurzi的建议**。 - - 新增:增加了异常错误的日志记录和查看功能,并支持导出为txt文件和同步复制到剪贴板。**感谢@Yurzi的建议**。 - - 新增:优化了用户注册时用户名和密码的输入规则,现在不强求必须是8位纯数字了,仅要求不包括空白字符,同时优化了不合法输入的提示信息。**感谢@chancat87的建议**。 - - 新增:增加了当用户密码不是8位纯数字时的加密和解密规则,已注册用户不受影响。 - - 新增:上传界面从网络链接获取图片时,加入了loading窗口提示,防止用户误以为程序卡死。 - - 优化:修改了部分窗口的提示语使其更加清晰。 - - 修复:修复了在图床管理界面,从剪贴板获取文件的时候,链接中带有?查询字符串时,无法正确获取文件名的问题。 - - 修复:修复了图床管理文件浏览界面,按文件大小排序时,排序结果不正确的问题。 -- 2022-10-27: **V1.8.3**: - - - 新增:上传页面重新设计,将主要功能放在了浮动按钮上,主页面用来显示上传列表,避免上传照片比较多时,一直卡在没有进度提示的loading窗口,单张拍照和连续上传两个功能仍沿用旧的上传方式。 - - 新增:用户登录页面重新设计,现在分为注册/登录和已登录两个页面,同时已登录页面显示用户信息和全部图床配置信息,并可以拉取云端配置和注销登录。 - - 新增:相册页面现在在切换页面的时候,会保留当前的页面状态,包括页数,选中状态等,同时上传了新图片或者清空了相册数据库后会自动触发相册刷新。 - - 新增:系统状态栏颜色调整为透明色,同时优化了部分页面APPBar的显示效果。 - - 优化:腾讯云COS二级页面的文件底部弹出栏,显示文件名时不会再显示目录前缀了。 - - 优化:部分文本显示现在可以被复制。 - - 修复:图床存储路径为一串空白字符时会导致上传错误的问题。 - - 修复:Github图床相册预览无法显示照片的问题和复制的url无法直接显示的问题。 -- 2022-10-27: **V1.8.2**: - - - 新增:上传页面默的认图床切换列表现在会使用不同的颜色来区分当前的默认图床。 - - 优化:弹出框统一为Cupertino样式。 - - 优化: 网络图片预览加入了加载中和加载失败的状态管理。 - - 优化:重命名了大部分代码文件和部分变量名,使其符合dart命名规范。 - - 优化:解决了绝大部分代码格式不规范问题,共计约450处。 - - 优化:优化了代码结构,提取出了一些公共方法,精简了代码量,共减少约2000行代码。 - - 修复:清空相册数据库页面,弹出框点击确认后,弹出框不会消失的问题。 -- 2022-10-25: **V1.8.1**: - - - 新增:图床仓库管理功能增加了对**SM.MS**的支持。 - - 新增:图床管理主页卡片现在可以拖动排序了,拖动后会自动保存顺序。 - - 优化: 文件全部删除后显示空目录提示背景,而不是空白。 - - 优化: 调整了部分界面里浮动按钮的位置和大小。 - - 修复:网络不佳的情况下,提前退出页面后,请求回调setState报错。 - - 修复: 如果在本地文件浏览目录里删除了已下载的文件,返回下载页面后删除下载任务会报错的问题。 - - 修复: 本地下载文件目录浏览页面里,右滑删除按钮点击无效的问题。 - - 修复: 图床文件浏览页面中,删除文件的时候,选中列表对应元素没有被同步移除的bug。 -- 2022-10-24: **V1.8.0**: - - - 增加了新的功能**图床仓库管理**,目前实现了对**腾讯云COS**的支持(*相当于内置了一个精简版的腾讯COSBrowser*),后续会陆续增加对其他图床的支持,主要功能有: - 1. 查看存储桶列表,支持创建和删除,修改访问权限,设置存储桶为腾讯云COS的默认存储桶。 - 2. 查看存储桶中的目录和文件列表,目录和文件都支持右滑删除,文件额外支持右滑分享连接。可新建文件夹,可设置目录为图床仓库的默认目录,文件可复制多种格式的链接,重命名,照片可以预览。 - 3. 存储桶管理页面和本地下载目录浏览页面均可按修改时间,文件名,文件大小,文件类型等排序。 - 4. 支持多选上传文件和照片,上传剪贴板内的网络链接。 - 5. 可批量下载和管理已下载文件,支持暂停和继续下载,支持删除已下载文件。 - 6. 支持管理本地下载目录,可重命名和删除已下载文件,支持预览照片和调用其它应用打开文件。 - - 修改了本地相册数据库的存储路径。 - - 从剪贴板链接获取网络图片时,加入了对空文本的处理。 - - 修改了部分字体,部分弹出框修改为ios样式。 - - UI细节优化。 - - 修复了图床api在请求失败时的错误处理。 -- 2022-10-17: **V1.7.6**: - - - 重写了路由管理,优化了路由跳转的体验,修改了跳转动画。 - - 主页/相册/设置页面现在不会左上角出现返回按钮,更加美观。 - - 优化了主页在深色主题下的显示效果。 -- 2022-10-15: **V1.7.5**: - - - 增加了对阿里云OSS的支持。 - - 增加了对又拍云存储的支持。 - - 增加了导出图床配置到剪贴板的功能,导出格式为json。 - - 增加了从剪贴板中的图片链接直接获取图片的功能,并且可以通过换行符分隔多个图片链接来一次性获取多张图片。 - - 增加了上传的时候自定义文件名的功能,使用 `{Y}`、`{y}`、`{m}`、`{d}`、`{uuid}`、`{md5}`等占位符,可选年月日,uuid,md5,随机字符串等任意组合来自定义文件名。 - - 增加了手动清除缓存的功能。 -- 2022-10-14: **V1.7.0**: - - - 增加了对腾讯云COS的支持。 - - 修改自定义链接格式的占位符为 `$fileName`和 `$url`,来和PicGo保持一致,同时修改了默认的自定义链接格式。 - - 修复了七牛云不设置存储路径的时候保存路径错误的问题。 - - 修复了github,imgur和七牛云导入二维码配置的时候可选参数默认值错误和七牛云数据库保存的数据错误的问题。 -- 2022-10-13: **V1.6.5**: - - - 增加了对七牛云的支持。 - - 调整了配置和图片上传/删除的时候的响应和连接超时时间设置数值。 - - 相册图片的外框默认透明色,选中的时候会有一个红色的边框提示。 -- 2022-10-12: **V1.6.0**: - - - 增加了对Imgur图床的支持,但是由于Imgur的限制,使用的时候需要配置手机代理,在个人手机上配合clash测试可用。 - - 加入了设置配置和图片上传/删除的时候的响应和连接超时时间设置,防止网络不好的情况下卡死。 - - 区分了相册显示的时候的图片地址和复制的时候的图片地址,改善相册加载图片的速度,例如兰空图床在相册小图中显示的是缩略图,预览大图的时候才会加载原图。 - - 修复了设置页面跳转到主页的时候,有时会先跳转到相册页面的问题。 - - 修复了注册用户的时候,同步创建本地相册数据库的代码没有执行的bug。 - - 修复了连续上传功能中,复制的链接的格式错误的bug。 - - 更改了登录页面UI,方便区分出是否已经登录。 -- 2022-10-11: **V1.5.5**: - - - 增加了扫码导入PicGo配置的功能,和PicGo进一步兼容。 - - 增加了对github图床的支持,在主页增加了切换默认上传图床的浮动按钮。 - - 增加了自定义复制链接的格式的功能,和PicGo的自定义格式一样,使用\${url}和\${fileName}来表示链接和文件名,可以在设置中自定义。 - - 增加了新的设置选项,可以选择在删除图片的时候是否同步删除网络端的图片(默认不删除)。 - - 重新整理了源代码文件架构,使得代码更加清晰,方便后续的更新和维护。 - - 修复了相册上翻页功能没有按预期作用的问题。 - - 修复了图片多选的时候,删除功能的bug,同时优化了动画显示,现在不会傻傻的等待了。 - - 修复了相册中显示的图床和默认上传图床不一致的时候无法删除网络端图片的bug。 - - 修复了选中状态会在翻页的时候保留的bug。 - - 修复了设置默认上传图床参数的时候,没有同步更改云端数据库记录的问题。 - - 优化了界面UI,修复了一些组件尺寸的问题。 -- 2022-10-09: **V1.5.0**: - - - 增加了**相册**功能,进一步对标了PicGo,现在PicHoro不仅是一款上传工具,也是一款图床管理工具。 - - 相册模块中实现了这些功能: - 1. 显示已经上传的图片,分页显示,每页12张,可以上划和下拉翻页。 - 2. 分图床显示,可选择显示某个图床的图片。 - 3. 实现了删除图片的功能,删除后会自动刷新相册,默认删除数据库记录和图床上的图片,可选择是否同步删除本地图片。 - 4. 实现了多选功能,可选择多张图片进行删除和复制指定格式的链接。 - 5. 可点击图片查看大图,双击图片可复制指定格式的链接,长按图片可弹出菜单,选择链接格式或者删除图片。 - 6. 相册中的图片数据保存在本地,通过APP内升级时不会丢失。 - - 增加了选择默认图床的页面,可以更直观的知道当前默认图床是哪个。 - - 修复了一些已知的bug。 -- 2022-10-07: **V1.4.1**: - - - 增加了对SM.MS图床的支持。 - - 修复了markdown链接的文件名错误的问题。 -- 2022-10-07: **V1.4.0**: - - - 增加了文件上传自动重命名的功能。 - - 增加了文件上传后自动复制链接的功能,同时可选url,html,markdown,bbcode和带链接的markdown等格式。 - - 增加了软件APP内自动更新的功能。 - - Github国内打开太慢,把项目地址页面换成更新日志页面。 - - 部分bug修复 -- 2022-10-06: **V1.3.1**: - - - 修复了已注册用户在新设备第一次登录的时候,无法正常登录的bug。 - - 修复了连续上传功能在退出的时候会卡在上传中的bug。 - - 已登录的设备在获取云端配置的时候不需要重新输入用户名和密码了。 - - 修复了部分代码小bug -- 2002-10-05: **V1.3.0**: - - - 重构了整个APP的代码架构,把所有的页面和功能性函数都放在了对应文件夹里,方便后续的维护和扩展,同时抽象了上传等功能的接口,后续增加图床时可以直接调用。 - - 增加了用户登录和拉取云端配置的功能,可以通过用户名和密码登录,将图床配置等保存在服务器上,这里现在用户本地用3DES加密然后再保存到数据库,除了用户名和图床名是明文外,其他的都是密文,这样可以保证用户的隐私。 - - 增加了软件主题切换的功能,增加了软件更新页面。 - - 新设计了软件的图标和启动画面,同时对软件的UI进行了一些优化。 - - 一些BUG修复 -- 2002-10-04: **V1.2.1**: - - - 增加了上传图片和配置图床时的等待动画。 - - 在设置页面增加了底部导航栏,修改了部分按钮的名字。 - - 调整了部分弹出式提示框的实现方式,修改为自动消失的小提示框,同时部分重要提示框禁止了点击背景消失。 - - 修复了项目地址页面打不开的问题。 - - 优化了部分代码。 -- 2002-10-03: **V1.2.0**: - - - 现在从相册里选择照片的时候可以多选了,上传功能也更新为批量上传。 - - 增加了新的设置页面,可以在设置页面里选择图床配制,项目地址和联系作者。 - - 重构了部分代码,为后续增加图床平台做准备。 - - 优化了页面布局,改变了部分UI。 -- 2002-10-03: **V1.1.0**: - - - 增加了对权限的主动获取,避免用户手动授予权限。 - - 增加了对拍照后自动上传并返回拍照界面的功能。 - - 增加了对各种异常的提醒。 - - 优化了弹出框的显示布局。 - - 优化了页面布局。 -- 2022-10-02: **V1.0.0**: - - - 项目初始化,完成基本的上传功能,目前仅支持兰空图床,需要手动授予存储和相机权限。 +- 新增:APP启动时现在会自动清理已下载的新版本安装包,避免占用过多空间。 +- 优化:修改了图床配置界面的图标UI。 +- 修复:修复了兰空图床二维码扫描没有反应的问题,感谢知乎用户@力子头的反馈 + +## 2022-11-08 **V1.8.7** + +- 新增:图床仓库管理功能增加了对**兰空图床**的支持 +- 优化:修改了相册和文件浏览页面缩略图的显示方式,从cover修改为fill。 +- 修复:修复了如果没有先单独下载一次文件,直接全部下载时,无法正常创建下载目录导致下载失败的问题。 + +## 2022-11-08 **V1.8.6** + +- 新增:图床仓库管理功能增加了对**七牛云**的支持 +- 新增:图床管理功能文件浏览新增了查看文件详情页,可查看文件的详细信息。 +- 新增:现在图床设置页面会自动填充已保存的配置信息,方便快速查看和更改配置。 +- 新增:阿里云新建文件夹时,加入了对文件夹名的预检查和处理,自动去除开头和结尾的'/',避免创建失败。 +- 优化:修改了部分UI的表现使其更加美观。 +- 修复:修复了七牛云亚太首尔地区上传api路径错误的问题。 +- 修复:修复了七牛云删除云端文件时,如果路径设置为根目录,会导致删除失败的问题。 +- 修复:修复了阿里云OSS上传文件时,如果路径设置为根目录,会导致上传失败的问题。 +- 修复: 修复了管理功能浏览文件时,网络错误会导致界面卡在loading的问题。 +- 修复:修复了部分界面UI错误,修复了日志界面的一些显示问题。 + +## 2022-11-04 **V1.8.5** + +- 新增:图床仓库管理功能增加了对**又拍云**的支持,需要单独登录一次又拍云的账号密码,实现了服务账号管理、存储桶管理到文件管理的完整支持。 +- 新增:重构了下载页面,现在同时显示上传和下载任务,并且现在重新进入上传/下载页面不会丢失任务了,并且现在可以单独删除任务了。 +- 优化:上传界面内用户未登录时,现在会提示用户登录后才能使用相关功能。 +- 优化:相册内删除图片时,如果云端删除失败,现在会提示用户并中止删除流程。 +- 优化:现在设置又拍云图床时,网站后缀不再是必选参数。 +- 优化:现在部分页面返回后会主动触发上级页面刷新,以保证数据的及时更新。 +- 优化: 目录内文件全部删除后现在正确显示空目录提示背景,而不是空白。 +- 优化:优化了注销登录的处理,现在会将所有用户设置重置为默认值。 +- 优化:修改了部分图标和文字,以及部分页面的布局。 +- 修复:修复了相册页面,来回切换图床会导致部分图床无法正常删除云端图片的问题。 +- 修复:修复了当图片路径中包含中文时,又拍云图床无法正常删除云端图片的问题。 +- 修复:修复了又拍云图床上传图片后,图片链接多了一个'/'的问题。 +- 修复:修复了未登录或者配置图床的时候,对应图床管理页面会卡住的问题。 +- 修复:修复了从剪贴板上传文件功能在部分情况下无法正常使用的问题。 +- 修复:修复了部分日志函数名记录错误的问题。 + +## 2022-11-01 **V1.8.4** + +- 新增:图床仓库管理功能增加了对**阿里云**的支持。 +- 新增:修改了上传文件时重命名的逻辑,现在不会同步重命名本地文件了。**感谢@Yurzi的建议**。 +- 新增:自定义文件重命名现在增加了不合规格式的检查,并且由于重命名逻辑的修改,现在可以使用'/'来同步新建文件夹了。**感谢@Yurzi的建议**。 +- 新增:增加了异常错误的日志记录和查看功能,并支持导出为txt文件和同步复制到剪贴板。**感谢@Yurzi的建议**。 +- 新增:优化了用户注册时用户名和密码的输入规则,现在不强求必须是8位纯数字了,仅要求不包括空白字符,同时优化了不合法输入的提示信息。**感谢@chancat8的建议**。 +- 新增:增加了当用户密码不是8位纯数字时的加密和解密规则,已注册用户不受影响。 +- 新增:上传界面从网络链接获取图片时,加入了loading窗口提示,防止用户误以为程序卡死。 +- 优化:修改了部分窗口的提示语使其更加清晰。 +- 修复:修复了在图床管理界面,从剪贴板获取文件的时候,链接中带有?查询字符串时,无法正确获取文件名的问题。 +- 修复:修复了图床管理文件浏览界面,按文件大小排序时,排序结果不正确的问题。 + +## 2022-10-27 **V1.8.3** + +- 新增:上传页面重新设计,将主要功能放在了浮动按钮上,主页面用来显示上传列表,避免上传照片比较多时,一直卡在没有进度提示的loading窗口,单张拍照和续上传两个功能仍沿用旧的上传方式。 +- 新增:用户登录页面重新设计,现在分为注册/登录和已登录两个页面,同时已登录页面显示用户信息和全部图床配置信息,并可以拉取云端配置和注销登录。 +- 新增:相册页面现在在切换页面的时候,会保留当前的页面状态,包括页数,选中状态等,同时上传了新图片或者清空了相册数据库后会自动触发相册刷新。 +- 新增:系统状态栏颜色调整为透明色,同时优化了部分页面APPBar的显示效果。 +- 优化:腾讯云COS二级页面的文件底部弹出栏,显示文件名时不会再显示目录前缀了。 +- 优化:部分文本显示现在可以被复制。 +- 修复:图床存储路径为一串空白字符时会导致上传错误的问题。 +- 修复:Github图床相册预览无法显示照片的问题和复制的url无法直接显示的问题。 + +## 2022-10-27 **V1.8.2** + +- 新增:上传页面默的认图床切换列表现在会使用不同的颜色来区分当前的默认图床。 +- 优化:弹出框统一为Cupertino样式。 +- 优化: 网络图片预览加入了加载中和加载失败的状态管理。 +- 优化:重命名了大部分代码文件和部分变量名,使其符合dart命名规范。 +- 优化:解决了绝大部分代码格式不规范问题,共计约450处。 +- 优化:优化了代码结构,提取出了一些公共方法,精简了代码量,共减少约2000行代码。 +- 修复:清空相册数据库页面,弹出框点击确认后,弹出框不会消失的问题。 + +## 2022-10-25 **V1.8.1** + +- 新增:图床仓库管理功能增加了对**SM.MS**的支持。 +- 新增:图床管理主页卡片现在可以拖动排序了,拖动后会自动保存顺序。 +- 优化: 文件全部删除后显示空目录提示背景,而不是空白。 +- 优化: 调整了部分界面里浮动按钮的位置和大小。 +- 修复:网络不佳的情况下,提前退出页面后,请求回调setState报错。 +- 修复: 如果在本地文件浏览目录里删除了已下载的文件,返回下载页面后删除下载任务会报错的问题。 +- 修复: 本地下载文件目录浏览页面里,右滑删除按钮点击无效的问题。 +- 修复: 图床文件浏览页面中,删除文件的时候,选中列表对应元素没有被同步移除的bug。 + +## 2022-10-24 **V1.8.0** + +- 增加了新的功能**图床仓库管理**,目前实现了对**腾讯云COS**的支持(*相当于内置了一个精简版的腾讯COSBrowser*),后续会陆续增加对其他图床的支持,主要能有: + 1. 查看存储桶列表,支持创建和删除,修改访问权限,设置存储桶为腾讯云COS的默认存储桶。 + 2. 查看存储桶中的目录和文件列表,目录和文件都支持右滑删除,文件额外支持右滑分享连接。可新建文件夹,可设置目录为图床仓库的默认目录,文件可复制多格式的链接,重命名,照片可以预览。 + 3. 存储桶管理页面和本地下载目录浏览页面均可按修改时间,文件名,文件大小,文件类型等排序。 + 4. 支持多选上传文件和照片,上传剪贴板内的网络链接。 + 5. 可批量下载和管理已下载文件,支持暂停和继续下载,支持删除已下载文件。 + 6. 支持管理本地下载目录,可重命名和删除已下载文件,支持预览照片和调用其它应用打开文件。 +- 修改了本地相册数据库的存储路径。 +- 从剪贴板链接获取网络图片时,加入了对空文本的处理。 +- 修改了部分字体,部分弹出框修改为ios样式。 +- UI细节优化。 +- 修复了图床api在请求失败时的错误处理。 + +## 2022-10-17 **V1.7.6** + +- 重写了路由管理,优化了路由跳转的体验,修改了跳转动画。 +- 主页/相册/设置页面现在不会左上角出现返回按钮,更加美观。 +- 优化了主页在深色主题下的显示效果。 + +## 2022-10-15 **V1.7.5** + +- 增加了对阿里云OSS的支持。 +- 增加了对又拍云存储的支持。 +- 增加了导出图床配置到剪贴板的功能,导出格式为json。 +- 增加了从剪贴板中的图片链接直接获取图片的功能,并且可以通过换行符分隔多个图片链接来一次性获取多张图片。 +- 增加了上传的时候自定义文件名的功能,使用 `{Y}`、`{y}`、`{m}`、`{d}`、`{uuid}`、`{md5}`等占位符,可选年月日,uuid,md5,随机字符串等任意组合来义文件名。 +- 增加了手动清除缓存的功能。 + +## 2022-10-14 **V1.7.0** + +- 增加了对腾讯云COS的支持。 +- 修改自定义链接格式的占位符为 `$fileName`和 `$url`,来和PicGo保持一致,同时修改了默认的自定义链接格式。 +- 修复了七牛云不设置存储路径的时候保存路径错误的问题。 +- 修复了github,imgur和七牛云导入二维码配置的时候可选参数默认值错误和七牛云数据库保存的数据错误的问题。 + +## 2022-10-13 **V1.6.5** + +- 增加了对七牛云的支持。 +- 调整了配置和图片上传/删除的时候的响应和连接超时时间设置数值。 +- 相册图片的外框默认透明色,选中的时候会有一个红色的边框提示。 + +## 2022-10-12 **V1.6.0** + +- 增加了对Imgur图床的支持,但是由于Imgur的限制,使用的时候需要配置手机代理,在个人手机上配合clash测试可用。 +- 加入了设置配置和图片上传/删除的时候的响应和连接超时时间设置,防止网络不好的情况下卡死。 +- 区分了相册显示的时候的图片地址和复制的时候的图片地址,改善相册加载图片的速度,例如兰空图床在相册小图中显示的是缩略图,预览大图的时候才会加载原图。 +- 修复了设置页面跳转到主页的时候,有时会先跳转到相册页面的问题。 +- 修复了注册用户的时候,同步创建本地相册数据库的代码没有执行的bug。 +- 修复了连续上传功能中,复制的链接的格式错误的bug。 +- 更改了登录页面UI,方便区分出是否已经登录。 + +## 2022-10-11 **V1.5.5** + +- 增加了扫码导入PicGo配置的功能,和PicGo进一步兼容。 +- 增加了对github图床的支持,在主页增加了切换默认上传图床的浮动按钮。 +- 增加了自定义复制链接的格式的功能,和PicGo的自定义格式一样,使用\${url}和\${fileName}来表示链接和文件名,可以在设置中自定义。 +- 增加了新的设置选项,可以选择在删除图片的时候是否同步删除网络端的图片(默认不删除)。 +- 重新整理了源代码文件架构,使得代码更加清晰,方便后续的更新和维护。 +- 修复了相册上翻页功能没有按预期作用的问题。 +- 修复了图片多选的时候,删除功能的bug,同时优化了动画显示,现在不会傻傻的等待了。 +- 修复了相册中显示的图床和默认上传图床不一致的时候无法删除网络端图片的bug。 +- 修复了选中状态会在翻页的时候保留的bug。 +- 修复了设置默认上传图床参数的时候,没有同步更改云端数据库记录的问题。 +- 优化了界面UI,修复了一些组件尺寸的问题。 + +## 2022-10-09 **V1.5.0** + +- 增加了**相册**功能,进一步对标了PicGo,现在PicHoro不仅是一款上传工具,也是一款图床管理工具。 +- 相册模块中实现了这些功能: + 1. 显示已经上传的图片,分页显示,每页12张,可以上划和下拉翻页。 + 2. 分图床显示,可选择显示某个图床的图片。 + 3. 实现了删除图片的功能,删除后会自动刷新相册,默认删除数据库记录和图床上的图片,可选择是否同步删除本地图片。 + 4. 实现了多选功能,可选择多张图片进行删除和复制指定格式的链接。 + 5. 可点击图片查看大图,双击图片可复制指定格式的链接,长按图片可弹出菜单,选择链接格式或者删除图片。 + 6. 相册中的图片数据保存在本地,通过APP内升级时不会丢失。 +- 增加了选择默认图床的页面,可以更直观的知道当前默认图床是哪个。 +- 修复了一些已知的bug。 + +## 2022-10-07 **V1.4.1** + +- 增加了对SM.MS图床的支持。 +- 修复了markdown链接的文件名错误的问题。 + +## 2022-10-07 **V1.4.0** + +- 增加了文件上传自动重命名的功能。 +- 增加了文件上传后自动复制链接的功能,同时可选url,html,markdown,bbcode和带链接的markdown等格式。 +- 增加了软件APP内自动更新的功能。 +- Github国内打开太慢,把项目地址页面换成更新日志页面。 +- 部分bug修复 + +## 2022-10-06 **V1.3.1** + +- 修复了已注册用户在新设备第一次登录的时候,无法正常登录的bug。 +- 修复了连续上传功能在退出的时候会卡在上传中的bug。 +- 已登录的设备在获取云端配置的时候不需要重新输入用户名和密码了。 +- 修复了部分代码小bug + +## 2002-10-05 **V1.3.0** + +- 重构了整个APP的代码架构,把所有的页面和功能性函数都放在了对应文件夹里,方便后续的维护和扩展,同时抽象了上传等功能的接口,后续增加图床时可以直接用。 +- 增加了用户登录和拉取云端配置的功能,可以通过用户名和密码登录,将图床配置等保存在服务器上,这里现在用户本地用3DES加密然后再保存到数据库,除了用户和图床名是明文外,其他的都是密文,这样可以保证用户的隐私。 +- 增加了软件主题切换的功能,增加了软件更新页面。 +- 新设计了软件的图标和启动画面,同时对软件的UI进行了一些优化。 +- 一些BUG修复 + +## 2002-10-04 **V1.2.1** + +- 增加了上传图片和配置图床时的等待动画。 +- 在设置页面增加了底部导航栏,修改了部分按钮的名字。 +- 调整了部分弹出式提示框的实现方式,修改为自动消失的小提示框,同时部分重要提示框禁止了点击背景消失。 +- 修复了项目地址页面打不开的问题。 +- 优化了部分代码。 + +## 2002-10-03 **V1.2.0** + +- 现在从相册里选择照片的时候可以多选了,上传功能也更新为批量上传。 +- 增加了新的设置页面,可以在设置页面里选择图床配制,项目地址和联系作者。 +- 重构了部分代码,为后续增加图床平台做准备。 +- 优化了页面布局,改变了部分UI。 + +## 2002-10-03 **V1.1.0** + +- 增加了对权限的主动获取,避免用户手动授予权限。 +- 增加了对拍照后自动上传并返回拍照界面的功能。 +- 增加了对各种异常的提醒。 +- 优化了弹出框的显示布局。 +- 优化了页面布局。 + +## 2022-10-02 **V1.0.0** + +- 项目初始化,完成基本的上传功能,目前仅支持兰空图床,需要手动授予存储和相机权限。 diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig index 9a4622d..22973a0 100644 --- a/ios/Flutter/Generated.xcconfig +++ b/ios/Flutter/Generated.xcconfig @@ -4,7 +4,7 @@ FLUTTER_APPLICATION_PATH=D:\githubRepo\myProject\PicHoro COCOAPODS_PARALLEL_CODE_SIGN=true FLUTTER_TARGET=lib\main.dart FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=2.1.0 +FLUTTER_BUILD_NAME=2.2.2 FLUTTER_BUILD_NUMBER=1 EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh index dc9beef..29804aa 100644 --- a/ios/Flutter/flutter_export_environment.sh +++ b/ios/Flutter/flutter_export_environment.sh @@ -5,7 +5,7 @@ export "FLUTTER_APPLICATION_PATH=D:\githubRepo\myProject\PicHoro" export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "FLUTTER_TARGET=lib\main.dart" export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=2.1.0" +export "FLUTTER_BUILD_NAME=2.2.2" export "FLUTTER_BUILD_NUMBER=1" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m index d02813b..3398a1c 100644 --- a/ios/Runner/GeneratedPluginRegistrant.m +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -24,16 +24,22 @@ @import camera_avfoundation; #endif +#if __has_include() +#import +#else +@import device_info_plus; +#endif + #if __has_include() #import #else @import file_picker; #endif -#if __has_include() -#import +#if __has_include() +#import #else -@import flutter_image_compress; +@import flutter_image_compress_common; #endif #if __has_include() @@ -66,10 +72,10 @@ @import package_info_plus; #endif -#if __has_include() -#import +#if __has_include() +#import #else -@import path_provider_ios; +@import path_provider_foundation; #endif #if __has_include() @@ -96,10 +102,10 @@ @import share_plus; #endif -#if __has_include() -#import +#if __has_include() +#import #else -@import shared_preferences_ios; +@import shared_preferences_foundation; #endif #if __has_include() @@ -126,10 +132,10 @@ @import video_player_avfoundation; #endif -#if __has_include() -#import +#if __has_include() +#import #else -@import wakelock; +@import wakelock_plus; #endif #if __has_include() @@ -144,6 +150,7 @@ + (void)registerWithRegistry:(NSObject*)registry { [AutoOrientationPlugin registerWithRegistrar:[registry registrarForPlugin:@"AutoOrientationPlugin"]]; [BarcodeScanPlugin registerWithRegistrar:[registry registrarForPlugin:@"BarcodeScanPlugin"]]; [CameraPlugin registerWithRegistrar:[registry registrarForPlugin:@"CameraPlugin"]]; + [FLTDeviceInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTDeviceInfoPlusPlugin"]]; [FilePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FilePickerPlugin"]]; [ImageCompressPlugin registerWithRegistrar:[registry registrarForPlugin:@"ImageCompressPlugin"]]; [FlutterVlcPlayerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterVlcPlayerPlugin"]]; @@ -151,17 +158,17 @@ + (void)registerWithRegistry:(NSObject*)registry { [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; [OpenFilePlugin registerWithRegistrar:[registry registrarForPlugin:@"OpenFilePlugin"]]; [FLTPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPackageInfoPlusPlugin"]]; - [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]]; + [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; [PhotoManagerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PhotoManagerPlugin"]]; [RUpgradePlugin registerWithRegistrar:[registry registrarForPlugin:@"RUpgradePlugin"]]; [FLTSharePlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharePlusPlugin"]]; - [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]]; + [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]]; [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; [SyncfusionFlutterPdfViewerPlugin registerWithRegistrar:[registry registrarForPlugin:@"SyncfusionFlutterPdfViewerPlugin"]]; [FLTURLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTURLLauncherPlugin"]]; [FLTVideoPlayerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTVideoPlayerPlugin"]]; - [WakelockPlugin registerWithRegistrar:[registry registrarForPlugin:@"WakelockPlugin"]]; + [WakelockPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"WakelockPlusPlugin"]]; [FLTWebViewFlutterPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTWebViewFlutterPlugin"]]; } diff --git a/lib/album/album_page.dart b/lib/album/album_page.dart index c54f8d0..f26f6c5 100644 --- a/lib/album/album_page.dart +++ b/lib/album/album_page.dart @@ -4,7 +4,7 @@ Modified under MIT license See file LICENSE of original project at https://github.com/PicGo/flutter-picgo */ -import 'dart:io' as io; +import 'package:universal_io/io.dart'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -476,9 +476,9 @@ class UploadedImagesState extends State with AutomaticKeepAliveC loadStateChanged: (state) => defaultLoadStateChanged(state, iconSize: 30), ) : Global.defaultShowedPBhost == 'PBhostExtend1' - ? io.File(currentShowedImagesDisplayAddressUrl[index]).existsSync() + ? File(currentShowedImagesDisplayAddressUrl[index]).existsSync() ? ExtendedImage.file( - io.File(currentShowedImagesDisplayAddressUrl[index]), + File(currentShowedImagesDisplayAddressUrl[index]), fit: BoxFit.fill, clearMemoryCacheIfFailed: true, height: 150, @@ -970,7 +970,7 @@ class UploadedImagesState extends State with AutomaticKeepAliveC await AlbumSQL.deleteData(Global.imageDBExtend!, Global.defaultShowedPBhost, showedImageId[imagesIndex[i] + (_currentPage * _perPageItemSize) - i]); try { - await io.File(showedImageDisplayAddressUrl[imagesIndex[i] + _currentPage * _perPageItemSize - i]).delete(); + await File(showedImageDisplayAddressUrl[imagesIndex[i] + _currentPage * _perPageItemSize - i]).delete(); } catch (e) { FLog.error( className: 'ImagePage', @@ -990,7 +990,7 @@ class UploadedImagesState extends State with AutomaticKeepAliveC if (deleteLocal) { try { - await io.File(showedImagePaths[imagesIndex[i] + (_currentPage * _perPageItemSize) - i]).delete(); + await File(showedImagePaths[imagesIndex[i] + (_currentPage * _perPageItemSize) - i]).delete(); } catch (e) { FLog.error( className: 'ImagePage', @@ -1048,7 +1048,7 @@ class UploadedImagesState extends State with AutomaticKeepAliveC await AlbumSQL.deleteData(Global.imageDBExtend!, Global.defaultShowedPBhost, showedImageId[index + (_currentPage * _perPageItemSize)]); try { - await io.File(showedImageDisplayAddressUrl[index + _currentPage * _perPageItemSize]).delete(); + await File(showedImageDisplayAddressUrl[index + _currentPage * _perPageItemSize]).delete(); } catch (e) { FLog.error( className: 'ImagePage', @@ -1068,7 +1068,7 @@ class UploadedImagesState extends State with AutomaticKeepAliveC if (deleteLocal) { try { - await io.File(showedImagePaths[index + _currentPage * _perPageItemSize]).delete(); + await File(showedImagePaths[index + _currentPage * _perPageItemSize]).delete(); } catch (e) { FLog.error( className: 'ImagePage', diff --git a/lib/api/alist_api.dart b/lib/api/alist_api.dart index 668b07a..4b16857 100644 --- a/lib/api/alist_api.dart +++ b/lib/api/alist_api.dart @@ -8,119 +8,121 @@ import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; class AlistImageUploadUtils { - static String currentClassName = "AlistImageUploadUtils"; //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: name), - }); - String uploadPath = configMap['uploadPath']; - String token = configMap['token']; - String today = getToday('yyyyMMdd'); - String alistToday = await Global.getTodayAlistUpdate(); - if (alistToday != today && token != '') { - var res = await AlistManageAPI.getToken(configMap['host'], configMap['alistusername'], configMap['password']); - if (res[0] == 'success') { - token = res[1]; - final alistConfig = AlistConfigModel( - configMap['host'], - configMap['alistusername'], - configMap['password'], - token, - uploadPath, - ); - final alistConfigJson = jsonEncode(alistConfig); - final alistConfigFile = await AlistConfigState().localFile; - alistConfigFile.writeAsString(alistConfigJson); - await Global.setTodayAlistUpdate(today); + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + }) async { + try { + String formatedURL = ''; + FormData formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(path, filename: name), + }); + String uploadPath = configMap['uploadPath']; + String token = configMap['token']; + String today = getToday('yyyyMMdd'); + String alistToday = await Global.getTodayAlistUpdate(); + if (alistToday != today && token != '') { + var res = await AlistManageAPI.getToken(configMap['host'], configMap['alistusername'], configMap['password']); + if (res[0] == 'success') { + token = res[1]; + final alistConfig = AlistConfigModel( + configMap['host'], + configMap['alistusername'], + configMap['password'], + token, + uploadPath, + ); + final alistConfigJson = jsonEncode(alistConfig); + final alistConfigFile = await AlistConfigState().localFile; + alistConfigFile.writeAsString(alistConfigJson); + await Global.setTodayAlistUpdate(today); + } else { + return ['failed']; + } + } + if (uploadPath == 'None') { + uploadPath = '/'; } else { + if (!uploadPath.startsWith('/')) { + uploadPath = '/$uploadPath'; + } + if (!uploadPath.endsWith('/')) { + uploadPath = '$uploadPath/'; + } + } + String filePath = uploadPath + name; + + BaseOptions options = setBaseOptions(); + File uploadFile = File(path); + int contentLength = await uploadFile.length().then((value) { + return value; + }); + options.headers = { + "Authorization": token, + "Content-Type": Global.multipartString, + "file-path": Uri.encodeComponent(filePath), + "Content-Length": contentLength, + }; + Dio dio = Dio(options); + String uploadUrl = configMap["host"] + "/api/fs/form"; + + var response = await dio.put(uploadUrl, data: formdata, onSendProgress: onSendProgress); + if (response.statusCode != 200 || response.data!['message'] != 'success') { return ['failed']; } - } - if (uploadPath == 'None') { - uploadPath = '/'; - } else { - if (!uploadPath.startsWith('/')) { - uploadPath = '/$uploadPath'; + String infoGetUrl = configMap["host"] + "/api/fs/get"; + String refreshUrl = configMap["host"] + "/api/fs/list"; + BaseOptions getOptions = setBaseOptions(); + getOptions.headers = { + "Authorization": configMap["token"], + "Content-Type": "application/json", + }; + Dio dioGet = Dio(getOptions); + Dio dioRefresh = Dio(getOptions); + Map getformData = { + "path": filePath, + }; + Map refreshListFormData = {"password": "", "page": 1, "per_page": 1, "path": uploadPath, "refresh": true}; + var refreshResponse = await dioRefresh.post(refreshUrl, data: refreshListFormData); + if (refreshResponse.statusCode != 200 || refreshResponse.data!['message'] != 'success') { + return ['failed']; } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; + var responseGet = await dioGet.post(infoGetUrl, data: getformData); + if (responseGet.statusCode != 200 || responseGet.data['message'] != 'success') { + return ['failed']; } - } - String filePath = uploadPath + name; - - BaseOptions options = setBaseOptions(); - File uploadFile = File(path); - int contentLength = await uploadFile.length().then((value) { - return value; - }); - options.headers = { - "Authorization": token, - "Content-Type": Global.multipartString, - "file-path": Uri.encodeComponent(filePath), - "Content-Length": contentLength, - }; - Dio dio = Dio(options); - String uploadUrl = configMap["host"] + "/api/fs/form"; - - try { - var response = await dio.put(uploadUrl, data: formdata); - if (response.statusCode == 200 && response.data!['message'] == 'success') { - String infoGetUrl = configMap["host"] + "/api/fs/get"; - String refreshUrl = configMap["host"] + "/api/fs/list"; - BaseOptions getOptions = setBaseOptions(); - getOptions.headers = { - "Authorization": configMap["token"], - "Content-Type": "application/json", - }; - Dio dioGet = Dio(getOptions); - Dio dioRefresh = Dio(getOptions); - Map getformData = { - "path": filePath, - }; - Map refreshListFormData = {"password": "", "page": 1, "per_page": 1, "path": uploadPath, "refresh": true}; - var refreshResponse = await dioRefresh.post(refreshUrl, data: refreshListFormData); - if (refreshResponse.statusCode == 200 && refreshResponse.data!['message'] == 'success') { - var responseGet = await dioGet.post(infoGetUrl, data: getformData); - if (responseGet.statusCode == 200 && responseGet.data['message'] == 'success') { - String returnUrl = responseGet.data!['data']['raw_url']; - //返回缩略图地址用来在相册显示 - String displayUrl = responseGet.data!['data']['thumb'] == "" || responseGet.data!['data']['thumb'] == null - ? returnUrl - : responseGet.data!['data']['thumb']; - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['sign'] = responseGet.data!['data']['sign']; - pictureKeyMap['uploadPath'] = uploadPath; - pictureKeyMap['filenames'] = name; - String pictureKey = jsonEncode(pictureKeyMap); - String hostPicUrl = responseGet.data!['data']['sign'] == "" || responseGet.data!['data']['sign'] == null - ? returnUrl - : configMap['host'] + '/d' + filePath + '?sign=' + responseGet.data!['data']['sign']; + String returnUrl = responseGet.data!['data']['raw_url']; + //返回缩略图地址用来在相册显示 + String displayUrl = responseGet.data!['data']['thumb'] == "" || responseGet.data!['data']['thumb'] == null + ? returnUrl + : responseGet.data!['data']['thumb']; + Map pictureKeyMap = Map.from(configMap); + pictureKeyMap['sign'] = responseGet.data!['data']['sign']; + pictureKeyMap['uploadPath'] = uploadPath; + pictureKeyMap['filenames'] = name; + String pictureKey = jsonEncode(pictureKeyMap); + String hostPicUrl = responseGet.data!['data']['sign'] == "" || responseGet.data!['data']['sign'] == null + ? returnUrl + : configMap['host'] + '/d' + filePath + '?sign=' + responseGet.data!['data']['sign']; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(hostPicUrl, name); - } else { - formatedURL = hostPicUrl; - } - return ["success", formatedURL, returnUrl, pictureKey, displayUrl, hostPicUrl]; - } else { - return ['failed']; - } - } + if (Global.isCopyLink == true) { + formatedURL = linkGenerateDict[Global.defaultLKformat]!(hostPicUrl, name); } else { - return ['failed']; + formatedURL = hostPicUrl; } + return ["success", formatedURL, returnUrl, pictureKey, displayUrl, hostPicUrl]; } catch (e) { - flogErr( - e, - { - 'path': path, - 'name': name, - }, - currentClassName, - "deleteApi", - ); + flogError( + e, + { + 'path': path, + 'name': name, + }, + "AlistImageUploadUtils", + "uploadApi"); return ['failed']; } } @@ -137,22 +139,21 @@ class AlistImageUploadUtils { String alistToday = await Global.getTodayAlistUpdate(); if (alistToday != today && token != '') { var res = await AlistManageAPI.getToken(configMap['host'], configMap['alistusername'], configMap['password']); - if (res[0] == 'success') { - token = res[1]; - final alistConfig = AlistConfigModel( - configMap['host'], - configMap['alistusername'], - configMap['password'], - token, - uploadPath, - ); - final alistConfigJson = jsonEncode(alistConfig); - final alistConfigFile = await AlistConfigState().localFile; - alistConfigFile.writeAsString(alistConfigJson); - await Global.setTodayAlistUpdate(today); - } else { + if (res[0] != 'success') { return ['failed']; } + token = res[1]; + final alistConfig = AlistConfigModel( + configMap['host'], + configMap['alistusername'], + configMap['password'], + token, + uploadPath, + ); + final alistConfigJson = jsonEncode(alistConfig); + final alistConfigFile = await AlistConfigState().localFile; + alistConfigFile.writeAsString(alistConfigJson); + await Global.setTodayAlistUpdate(today); } BaseOptions options = setBaseOptions(); options.headers = { @@ -163,20 +164,19 @@ class AlistImageUploadUtils { String deleteUrl = configMapFromPictureKey["host"] + "/api/fs/remove"; try { var response = await dio.post(deleteUrl, data: formdata); - if (response.statusCode == 200 && response.data!['message'] == "success") { - return [ - "success", - ]; - } else { + if (response.statusCode != 200 || response.data!['message'] != "success") { return ['failed']; } + return ["success"]; } catch (e) { - flogErr( - e, - {}, - currentClassName, - "deleteApi", - ); + flogError( + e, + { + 'deleteMap': deleteMap, + 'configMap': configMap, + }, + "AlistImageUploadUtils", + "deleteApi"); return ['failed']; } } diff --git a/lib/api/aliyun_api.dart b/lib/api/aliyun_api.dart index 5539ae7..8ae2aea 100644 --- a/lib/api/aliyun_api.dart +++ b/lib/api/aliyun_api.dart @@ -2,78 +2,79 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:path/path.dart' as my_path; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; class AliyunImageUploadUtils { - //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String keyId = configMap['keyId']; - String keySecret = configMap['keySecret']; - String bucket = configMap['bucket']; - String area = configMap['area']; - String aliyunpath = configMap['path']; - String customUrl = configMap['customUrl']; - String options = configMap['options']; - //格式化 - if (customUrl != "None") { - if (!customUrl.startsWith(RegExp(r'http(s)?://'))) { - customUrl = 'http://$customUrl'; + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { + try { + String keyId = configMap['keyId'] ?? ''; + String keySecret = configMap['keySecret'] ?? ''; + String bucket = configMap['bucket'] ?? ''; + String area = configMap['area'] ?? ''; + String aliyunpath = configMap['path'] ?? ''; + String customUrl = configMap['customUrl'] ?? ''; + String options = configMap['options'] ?? ''; + //格式化 + if (customUrl != "None") { + if (!customUrl.startsWith(RegExp(r'http(s)?://'))) { + customUrl = 'http://$customUrl'; + } } - } - //格式化 - if (aliyunpath != 'None') { - aliyunpath = '${aliyunpath.replaceAll(RegExp(r'^/*'), '').replaceAll(RegExp(r'/*$'), '')}/'; - } - String host = '$bucket.$area.aliyuncs.com'; - //云存储的路径 - String urlpath = ''; - //阿里云不能以/开头 - if (aliyunpath != 'None') { - urlpath = '$aliyunpath$name'; - } else { - urlpath = name; - } + //格式化 + if (aliyunpath != 'None') { + aliyunpath = '${aliyunpath.replaceAll(RegExp(r'^/*'), '').replaceAll(RegExp(r'/*$'), '')}/'; + } + String host = '$bucket.$area.aliyuncs.com'; + //云存储的路径 阿里云不能以/开头 + String urlpath = aliyunpath != 'None' ? '$aliyunpath$name' : name; + + Map uploadPolicy = { + "expiration": "2034-12-01T12:00:00.000Z", + "conditions": [ + {"bucket": bucket}, + ["content-length-range", 0, 104857600], + {"key": urlpath} + ] + }; + String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); + String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); + Map formMap = { + 'key': urlpath, + 'OSSAccessKeyId': keyId, + 'policy': base64Policy, + 'Signature': singature, + 'file': await MultipartFile.fromFile(path, filename: my_path.basename(name)), + }; + if (getContentType(my_path.extension(path)) != null) { + formMap['x-oss-content-type'] = getContentType(my_path.extension(path)); + } + FormData formData = FormData.fromMap(formMap); + BaseOptions baseoptions = setBaseOptions(); + File uploadFile = File(path); + String contentLength = await uploadFile.length().then((value) { + return value.toString(); + }); + baseoptions.headers = { + 'Host': host, + 'Content-Type': Global.multipartString, + 'Content-Length': contentLength, + }; + Dio dio = Dio(baseoptions); - Map uploadPolicy = { - "expiration": "2034-12-01T12:00:00.000Z", - "conditions": [ - {"bucket": bucket}, - ["content-length-range", 0, 104857600], - {"key": urlpath} - ] - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); - Map formMap = { - 'key': urlpath, - 'OSSAccessKeyId': keyId, - 'policy': base64Policy, - 'Signature': singature, - 'file': await MultipartFile.fromFile(path, filename: name), - }; - if (getContentType(my_path.extension(path)) != null) { - formMap['x-oss-content-type'] = getContentType(my_path.extension(path)); - } - FormData formData = FormData.fromMap(formMap); - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(path); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - try { var response = await dio.post( 'https://$host', data: formData, + onSendProgress: onSendProgress, + cancelToken: cancelToken, ); if (response.statusCode == 204) { @@ -112,26 +113,15 @@ class AliyunImageUploadUtils { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "AliyunImageUploadUtils", + "uploadApi"); + return ['failed']; } } @@ -178,52 +168,16 @@ class AliyunImageUploadUtils { }; Dio dio = Dio(baseOptions); - try { - var response = await dio.delete( - deleteHost, - ); - if (response.statusCode == 204) { - return [ - "success", - ]; - } else { - return ["failed"]; - } - } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + var response = await dio.delete( + deleteHost, + ); + if (response.statusCode != 204) { + return ["failed"]; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError(e, {}, "AliyunImageUploadUtils", "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/aws_api.dart b/lib/api/aws_api.dart index 31ad675..b03229b 100644 --- a/lib/api/aws_api.dart +++ b/lib/api/aws_api.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:io'; -import 'package:f_logs/f_logs.dart'; import 'package:flutter/foundation.dart'; import 'package:minio/minio.dart'; import 'package:path/path.dart' as my_path; @@ -92,15 +91,15 @@ class AwsImageUploadUtils { String pictureKey = jsonEncode(pictureKeyMap); return ["success", formatedURL, returnUrl, pictureKey, displayUrl]; } catch (e) { - FLog.error( - className: "AwsImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ + flogError( + e, + { 'path': path, 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return [e.toString()]; + }, + "AwsImageUploadUtils", + "uploadApi"); + return ['failed']; } } @@ -150,15 +149,14 @@ class AwsImageUploadUtils { return ['success']; } catch (e) { - FLog.error( - className: "AwsImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ + flogError( + e, + { 'fileName': fileName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - - return [e.toString()]; + }, + "AwsImageUploadUtils", + "deleteApi"); + return ['failed']; } } } diff --git a/lib/api/ftp_api.dart b/lib/api/ftp_api.dart index 737217f..81b2e30 100644 --- a/lib/api/ftp_api.dart +++ b/lib/api/ftp_api.dart @@ -12,18 +12,18 @@ import 'package:horopic/utils/global.dart'; class FTPImageUploadUtils { //上传接口 static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - String ftpHost = configMap["ftpHost"]; - String ftpPort = configMap["ftpPort"]; - String ftpUser = configMap["ftpUser"]; - String ftpPassword = configMap["ftpPassword"]; - String ftpType = configMap["ftpType"]; - String isAnonymous = configMap["isAnonymous"]; - String uploadPath = configMap["uploadPath"]; - String? ftpCustomUrl = configMap["ftpCustomUrl"]; - String? ftpWebPath = configMap["ftpWebPath"]; - if (ftpType == 'SFTP') { - try { + try { + String formatedURL = ''; + String ftpHost = configMap["ftpHost"] ?? ''; + String ftpPort = configMap["ftpPort"] ?? ''; + String ftpUser = configMap["ftpUser"] ?? ''; + String ftpPassword = configMap["ftpPassword"] ?? ''; + String ftpType = configMap["ftpType"] ?? ''; + String isAnonymous = configMap["isAnonymous"].toString(); + String uploadPath = configMap["uploadPath"] ?? ''; + String? ftpCustomUrl = configMap["ftpCustomUrl"] ?? ''; + String? ftpWebPath = configMap["ftpWebPath"] ?? ''; + if (ftpType == 'SFTP') { final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort.toString())); final client = SSHClient( socket, @@ -106,27 +106,15 @@ class FTPImageUploadUtils { uploadPath, '$ftpCachePath/$thumbnailFileName' ]; - } catch (e) { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "uploadApiSFTP", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; - } - } else if (ftpType == 'FTP') { - FTPConnect ftpConnect; - if (isAnonymous == 'true') { - ftpConnect = FTPConnect(ftpHost, port: int.parse(ftpPort), securityType: SecurityType.FTP); - } else { - ftpConnect = FTPConnect(ftpHost, - port: int.parse(ftpPort), user: ftpUser, pass: ftpPassword, securityType: SecurityType.FTP); - } + } else if (ftpType == 'FTP') { + FTPConnect ftpConnect; + if (isAnonymous == 'true') { + ftpConnect = FTPConnect(ftpHost, port: int.parse(ftpPort), securityType: SecurityType.FTP); + } else { + ftpConnect = FTPConnect(ftpHost, + port: int.parse(ftpPort), user: ftpUser, pass: ftpPassword, securityType: SecurityType.FTP); + } - try { var connectResult = await ftpConnect.connect(); if (connectResult == true) { if (uploadPath == 'None') { @@ -234,17 +222,17 @@ class FTPImageUploadUtils { dataLogType: DataLogType.ERRORS.toString()); return ['failed']; } - } catch (e) { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "uploadApiFTP", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; } + } catch (e) { + FLog.error( + className: "FTPImageUploadUtils", + methodName: "uploadApiFTP", + text: formatErrorMessage({ + 'path': path, + 'name': name, + }, e.toString()), + dataLogType: DataLogType.ERRORS.toString()); + return ['failed']; } } @@ -260,37 +248,28 @@ class FTPImageUploadUtils { String name = deleteMap['name']; try { if (ftpType == 'SFTP') { - try { - final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort.toString())); - final client = SSHClient( - socket, - username: ftpUser, - onPasswordRequest: () { - return ftpPassword; - }, - ); - final sftp = await client.sftp(); - if (uploadPath == 'None') { - uploadPath = '/'; - } - if (!uploadPath.startsWith('/')) { - uploadPath = '/$uploadPath'; - } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; - } - String urlPath = uploadPath + name; - await sftp.remove(urlPath); - client.close(); - return ['success']; - } catch (e) { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort.toString())); + final client = SSHClient( + socket, + username: ftpUser, + onPasswordRequest: () { + return ftpPassword; + }, + ); + final sftp = await client.sftp(); + if (uploadPath == 'None') { + uploadPath = '/'; + } + if (!uploadPath.startsWith('/')) { + uploadPath = '/$uploadPath'; } + if (!uploadPath.endsWith('/')) { + uploadPath = '$uploadPath/'; + } + String urlPath = uploadPath + name; + await sftp.remove(urlPath); + client.close(); + return ['success']; } else if (ftpType == 'FTP') { FTPConnect ftpConnect; if (isAnonymous == 'true') { @@ -299,39 +278,18 @@ class FTPImageUploadUtils { ftpConnect = FTPConnect(ftpHost, port: int.parse(ftpPort), user: ftpUser, pass: ftpPassword, securityType: SecurityType.FTP); } - try { - var connectResult = await ftpConnect.connect(); - if (connectResult == true) { - await ftpConnect.changeDirectory(uploadPath); - bool res = await ftpConnect.deleteFile(name); - if (res == true) { - return [ - 'success', - ]; - } else { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, 'delete failed'), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; - } - } else { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "deleteApiFTP", - text: formatErrorMessage({}, 'connect failed'), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; - } - } catch (e) { - FLog.error( - className: "FTPImageUploadUtils", - methodName: "deleteApiFTP", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return ['failed']; + + var connectResult = await ftpConnect.connect(); + if (connectResult != true) { + throw Exception('connect failed'); + } + + await ftpConnect.changeDirectory(uploadPath); + bool res = await ftpConnect.deleteFile(name); + if (res != true) { + throw Exception('delete failed'); } + return ['success']; } } catch (e) { FLog.error( @@ -339,7 +297,7 @@ class FTPImageUploadUtils { methodName: "deleteApi", text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); - return [e.toString()]; + return ['failed']; } } } diff --git a/lib/api/github_api.dart b/lib/api/github_api.dart index 328e5f1..6083c03 100644 --- a/lib/api/github_api.dart +++ b/lib/api/github_api.dart @@ -1,99 +1,93 @@ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; class GithubImageUploadUtils { //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - String base64Image = base64Encode(File(path).readAsBytesSync()); + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + }) async { + try { + String formatedURL = ''; + String base64Image = base64Encode(File(path).readAsBytesSync()); - Map queryBody = { - 'message': 'uploaded by PicHoro app', - 'content': base64Image, - 'branch': configMap["branch"], //分支 - }; + Map queryBody = { + 'message': 'uploaded by PicHoro app', + 'content': base64Image, + 'branch': configMap["branch"], //分支 + }; - BaseOptions options = setBaseOptions(); + BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Accept": "application/vnd.github+json", - }; - String trimedPath = configMap['storePath'].toString().trim(); - trimedPath = trimedPath.replaceAll(RegExp(r'^/+'), '').replaceAll(RegExp(r'/+$'), ''); - Dio dio = Dio(options); - String uploadUrl = ''; - if (trimedPath == 'None') { - uploadUrl = "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$name"; - } else { - uploadUrl = - "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$trimedPath/$name"; - } - try { - var response = await dio.put(uploadUrl, data: jsonEncode(queryBody)); - if (response.statusCode == 200 || response.statusCode == 201) { - String returnUrl = response.data!['content']['html_url']; - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['sha'] = response.data!['content']['sha']; - String pictureKey = jsonEncode(pictureKeyMap); - String downloadUrl = ''; - if (configMap['customDomain'] != 'None') { - if (configMap['customDomain'].toString().endsWith('/')) { - String trimedCustomDomain = - configMap['customDomain'].toString().substring(0, configMap['customDomain'].toString().length - 1); - if (trimedPath == 'None') { - downloadUrl = '$trimedCustomDomain$name'; - } else { - downloadUrl = '$trimedCustomDomain$trimedPath/$name'; - } + options.headers = { + "Authorization": configMap["token"], + "Accept": "application/vnd.github+json", + }; + String trimedPath = configMap['storePath'].toString().trim(); + trimedPath = trimedPath.replaceAll(RegExp(r'^/+'), '').replaceAll(RegExp(r'/+$'), ''); + Dio dio = Dio(options); + String uploadUrl = ''; + if (trimedPath == 'None') { + uploadUrl = "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$name"; + } else { + uploadUrl = + "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$trimedPath/$name"; + } + + var response = await dio.put(uploadUrl, data: jsonEncode(queryBody), onSendProgress: onSendProgress); + if (response.statusCode != 200 && response.statusCode != 201) { + return ["failed"]; + } + + String returnUrl = response.data!['content']['html_url']; + Map pictureKeyMap = Map.from(configMap); + pictureKeyMap['sha'] = response.data!['content']['sha']; + String pictureKey = jsonEncode(pictureKeyMap); + String downloadUrl = ''; + if (configMap['customDomain'] != 'None') { + if (configMap['customDomain'].toString().endsWith('/')) { + String trimedCustomDomain = + configMap['customDomain'].toString().substring(0, configMap['customDomain'].toString().length - 1); + if (trimedPath == 'None') { + downloadUrl = '$trimedCustomDomain$name'; } else { - if (trimedPath == 'None') { - downloadUrl = '${configMap['customDomain']}/$name'; - } else { - downloadUrl = '${configMap['customDomain']}/$trimedPath/$name'; - } + downloadUrl = '$trimedCustomDomain$trimedPath/$name'; } } else { - downloadUrl = response.data!['content']['download_url']; - } - if (!downloadUrl.startsWith('http') && !downloadUrl.startsWith('https')) { - downloadUrl = 'http://$downloadUrl'; - } - //复制的链接地址应该是downloadUrl - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(downloadUrl, name); - } else { - formatedURL = downloadUrl; + if (trimedPath == 'None') { + downloadUrl = '${configMap['customDomain']}/$name'; + } else { + downloadUrl = '${configMap['customDomain']}/$trimedPath/$name'; + } } - return ["success", formatedURL, returnUrl, pictureKey, downloadUrl]; } else { - return ["failed"]; + downloadUrl = response.data!['content']['download_url']; } - } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); + if (!downloadUrl.startsWith('http') && !downloadUrl.startsWith('https')) { + downloadUrl = 'http://$downloadUrl'; + } + //复制的链接地址应该是downloadUrl + if (Global.isCopyLink == true) { + formatedURL = linkGenerateDict[Global.defaultLKformat]!(downloadUrl, name); } else { - FLog.error( - className: "GithubImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); + formatedURL = downloadUrl; } - return [e.toString()]; + return ["success", formatedURL, returnUrl, pictureKey, downloadUrl]; + } catch (e) { + flogError( + e, + { + 'path': path, + 'name': name, + }, + "GithubImageUploadUtils", + "uploadApi"); + return ["failed"]; } } @@ -125,28 +119,20 @@ class GithubImageUploadUtils { } try { var response = await dio.delete(deleteUrl, data: jsonEncode(formdata)); - if (response.statusCode == 200) { - return [ - "success", - ]; - } else { + if (response.statusCode != 200) { return ["failed"]; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': deleteMap['path'], + 'name': deleteMap['name'], + }, + "GithubImageUploadUtils", + "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/imgur_api.dart b/lib/api/imgur_api.dart index 28968c9..f3b5a0a 100644 --- a/lib/api/imgur_api.dart +++ b/lib/api/imgur_api.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; @@ -9,74 +8,69 @@ import 'package:horopic/utils/dio_proxy_adapter.dart'; class ImgurImageUploadUtils { //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - String base64Image = base64Encode(File(path).readAsBytesSync()); + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { + try { + String formatedURL = ''; + String base64Image = base64Encode(File(path).readAsBytesSync()); - FormData formdata = FormData.fromMap({ - "image": base64Image, - }); + FormData formdata = FormData.fromMap({ + "image": base64Image, + }); - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": "Client-ID ${configMap["clientId"]}", - }; - Dio dio = Dio(options); - String proxy = configMap["proxy"]; - String proxyClean = ''; - if (proxy != 'None') { - if (proxy.startsWith(RegExp(r'^https?://'))) { - proxyClean = proxy.split('://')[1]; - } else { - proxyClean = proxy; - } - dio.httpClientAdapter = useProxy(proxyClean); - } - //官方文档里写的是https://api.imgur.com/3/upload emmmmmm - String uploadUrl = "https://api.imgur.com/3/image"; - try { - var response = await dio.post(uploadUrl, data: formdata); - if (response.statusCode == 200 && response.data!['success'] == true) { - String returnUrl = response.data!['data']['link']; - Map pictureKeyMap = { - 'clientId': configMap['clientId'], - 'deletehash': response.data!['data']['deletehash'], - }; - String pictureKey = jsonEncode(pictureKeyMap); - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, name); + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": "Client-ID ${configMap["clientId"]}", + }; + Dio dio = Dio(options); + String proxy = configMap["proxy"]; + String proxyClean = ''; + if (proxy != 'None') { + if (proxy.startsWith(RegExp(r'^https?://'))) { + proxyClean = proxy.split('://')[1]; } else { - formatedURL = returnUrl; + proxyClean = proxy; } - //相册显示地址用cdn加速,但是复制的时候还是用原图地址 - //https://search.pstatic.net/common?src= + dio.httpClientAdapter = useProxy(proxyClean); + } + //官方文档里写的是https://api.imgur.com/3/upload emmmmmm + String uploadUrl = "https://api.imgur.com/3/image"; - String cdnUrl = 'https://search.pstatic.net/common?src=$returnUrl'; - return ["success", formatedURL, returnUrl, pictureKey, cdnUrl]; - } else { + var response = + await dio.post(uploadUrl, data: formdata, onSendProgress: onSendProgress, cancelToken: cancelToken); + if (response.statusCode != 200 || response.data!['success'] != true) { return ["failed"]; } - } catch (e) { - if (e is DioException) { - FLog.error( - className: "ImgurImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); + + String returnUrl = response.data!['data']['link']; + Map pictureKeyMap = { + 'clientId': configMap['clientId'], + 'deletehash': response.data!['data']['deletehash'], + }; + String pictureKey = jsonEncode(pictureKeyMap); + if (Global.isCopyLink == true) { + formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, name); } else { - FLog.error( - className: "ImgurImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); + formatedURL = returnUrl; } - return [e.toString()]; + + String cdnUrl = returnUrl; + return ["success", formatedURL, returnUrl, pictureKey, cdnUrl]; + } catch (e) { + flogError( + e, + { + 'path': path, + 'name': name, + }, + "ImgurImageUploadUtils", + "uploadApi"); + return ['failed']; } } @@ -103,26 +97,13 @@ class ImgurImageUploadUtils { try { var response = await dio.delete(deleteUrl); - if (response.statusCode == 200 && response.data!['success'] == true) { - return ["success"]; - } else { + if (response.statusCode != 200 || response.data!['success'] != true) { return ["failed"]; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "ImgurImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "ImgurImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError(e, {}, "ImgurImageUploadUtils", "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/lskypro_api.dart b/lib/api/lskypro_api.dart index d15e874..96a9a03 100644 --- a/lib/api/lskypro_api.dart +++ b/lib/api/lskypro_api.dart @@ -1,43 +1,50 @@ import 'package:dio/dio.dart'; import 'dart:convert'; -import 'package:f_logs/f_logs.dart'; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; +import 'package:path/path.dart' as my_path; //兰空V2 class LskyproImageUploadUtils { //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: name), - }); - String albumId = configMap['album_id']; - if (configMap["strategy_id"] == "None") { - formdata = FormData.fromMap({}); - } else if (albumId == 'None') { - formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: name), - "strategy_id": configMap["strategy_id"], - }); - } else { - formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: name), - "strategy_id": configMap["strategy_id"], - "album_id": albumId, - }); - } - - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Accept": "application/json", - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); - String uploadUrl = configMap["host"] + "/api/v1/upload"; - + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { try { + String formatedURL = ''; + FormData formdata; + String albumId = configMap['album_id'] ?? 'None'; + String strategyId = configMap['strategy_id'] ?? 'None'; + if (albumId == 'None' && strategyId == 'None') { + formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(path, filename: my_path.basename(name)), + }); + } else { + if (strategyId == 'None') { + formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(path, filename: my_path.basename(name)), + "album_id": albumId, + }); + } else { + formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(path, filename: my_path.basename(name)), + "strategy_id": configMap["strategy_id"], + }); + } + } + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": configMap["token"], + "Accept": "application/json", + "Content-Type": "multipart/form-data", + }; + Dio dio = Dio(options); + String uploadUrl = configMap["host"] + "/api/v1/upload"; + var response = await dio.post(uploadUrl, data: formdata); if (response.statusCode == 200 && response.data!['status'] == true) { String returnUrl = response.data!['data']['links']['url']; @@ -57,26 +64,15 @@ class LskyproImageUploadUtils { return ["failed"]; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "LskyproImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "LskyproImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "LskyproImageUploadUtils", + "uploadApi"); + return ["failed"]; } } @@ -95,27 +91,12 @@ class LskyproImageUploadUtils { try { var response = await dio.delete(deleteUrl, data: formdata); if (response.statusCode == 200 && response.data!['status'] == true) { - return [ - "success", - ]; - } else { - return ["failed"]; + return ["success"]; } + return ["failed"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "LskyproImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "LskyproImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError(e, {}, "LskyproImageUploadUtils", "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/qiniu_api.dart b/lib/api/qiniu_api.dart index 56ad76d..cabe4a5 100644 --- a/lib/api/qiniu_api.dart +++ b/lib/api/qiniu_api.dart @@ -2,8 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; -import 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart'; -import 'package:f_logs/f_logs.dart'; +import 'package:path/path.dart' as my_path; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; @@ -77,55 +76,78 @@ class QiniuImageUploadUtils { } //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String bucket = configMap['bucket']; - String url = configMap['url']; - String options = configMap['options']; - String qiniupath = configMap['path']; - - if (!url.startsWith('http') && !url.startsWith('https')) { - url = 'http://$url'; - } - if (url.endsWith('/')) { - url = url.substring(0, url.length - 1); - } - - //不为None才处理 - if (qiniupath != 'None') { - if (qiniupath.startsWith('/')) { - qiniupath = qiniupath.substring(1); + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { + try { + String accessKey = configMap['accessKey'] ?? ''; + String secretKey = configMap['secretKey'] ?? ''; + String bucket = configMap['bucket'] ?? ''; + String url = configMap['url'] ?? ''; + String area = configMap['area'] ?? ''; + String options = configMap['options'] ?? ''; + String qiniupath = configMap['path'] ?? 'None'; + + if (!url.startsWith('http') && !url.startsWith('https')) { + url = 'http://$url'; } - if (!qiniupath.endsWith('/')) { - qiniupath = '$qiniupath/'; + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); } - } - String key = name; - - String urlSafeBase64EncodePutPolicy = geturlSafeBase64EncodePutPolicy(bucket, key, qiniupath); - String uploadToken = getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); - - try { - Storage storage = Storage( - config: Config( - retryLimit: 5, - )); - var uploadResult = await storage.putFile(File(path), uploadToken); - - if (uploadResult.key == key || uploadResult.key == '$qiniupath$key') { + String urlpath = ''; + //不为None才处理 + if (qiniupath != 'None') { + if (qiniupath.startsWith('/')) { + qiniupath = qiniupath.substring(1); + } + if (!qiniupath.endsWith('/')) { + qiniupath = '$qiniupath/'; + } + urlpath = '$qiniupath$name'; + } else { + urlpath = name; + } + String key = name; + + String urlSafeBase64EncodePutPolicy = geturlSafeBase64EncodePutPolicy(bucket, key, qiniupath); + String uploadToken = getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); + String host = QiniuImageUploadUtils.areaHostMap[area]!; + FormData formData = FormData.fromMap({ + "key": urlpath, + "fileName": name, + "token": uploadToken, + "file": await MultipartFile.fromFile(path, filename: my_path.basename(path)), + }); + BaseOptions baseoptions = setBaseOptions(); + //不需要加Content-Type,host,Content-Length + baseoptions.headers = { + 'Authorization': 'UpToken $uploadToken', + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( + host, + data: formData, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); + + if (response.statusCode == HttpStatus.ok) { String returnUrl = ''; String displayUrl = ''; if (options == 'None') { - returnUrl = '$url/${uploadResult.key}'; - displayUrl = '$url/${uploadResult.key}?imageView2/2/w/500/h/500'; + returnUrl = '$url/${response.data['key']}'; + displayUrl = '$url/${response.data['key']}?imageView2/2/w/500/h/500'; } else { if (!options.startsWith('?')) { options = '?$options'; } - returnUrl = '$url/${uploadResult.key}$options'; - displayUrl = '$url/${uploadResult.key}$options'; + returnUrl = '$url/${response.data['key']}$options'; + displayUrl = '$url/${response.data['key']}$options'; } String formatedURL = ''; if (Global.isCopyLink == true) { @@ -140,26 +162,15 @@ class QiniuImageUploadUtils { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuUpload", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuUpload", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "QiniuImageUploadUtils", + "uploadApi"); + return ['failed']; } } @@ -200,32 +211,19 @@ class QiniuImageUploadUtils { try { var response = await dio.delete(deleteUrl); - if (response.statusCode == 200) { - return [ - "success", - ]; - } else { + if (response.statusCode != 200) { return ["failed"]; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuUpload", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuUpload", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'fileName': fileName, + }, + "QiniuImageUploadUtils", + "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/smms_api.dart b/lib/api/smms_api.dart index 2e9c591..d59cbb8 100644 --- a/lib/api/smms_api.dart +++ b/lib/api/smms_api.dart @@ -1,27 +1,37 @@ import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; +import 'package:path/path.dart' as my_path; class SmmsImageUploadUtils { //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - FormData formdata = FormData.fromMap({ - "smfile": await MultipartFile.fromFile(path, filename: name), - "format": "json", - }); - - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); - String uploadUrl = "https://smms.app/api/v2/upload"; - + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { try { - var response = await dio.post(uploadUrl, data: formdata); + String formatedURL = ''; + FormData formdata = FormData.fromMap({ + "smfile": await MultipartFile.fromFile(path, filename: my_path.basename(name)), + "format": "json", + }); + + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": configMap["token"], + "Content-Type": "multipart/form-data", + }; + Dio dio = Dio(options); + String uploadUrl = "https://smms.app/api/v2/upload"; + var response = await dio.post( + uploadUrl, + data: formdata, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); if (response.statusCode == 200 && response.data!['success'] == true) { String returnUrl = response.data!['data']['url']; String pictureKey = response.data!['data']['hash']; @@ -35,26 +45,15 @@ class SmmsImageUploadUtils { return ["failed"]; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "SmmsImageUploadUtils", + "uploadApi"); + return ["failed"]; } } @@ -70,30 +69,16 @@ class SmmsImageUploadUtils { }; Dio dio = Dio(options); String deleteUrl = "https://smms.app/api/v2/delete/${deleteMap["pictureKey"]}"; - //String uploadUrl = "https://sm.ms/api/v2/delete/:hash"; //主要接口,国内访问不了 try { var response = await dio.get(deleteUrl, queryParameters: formdata); if (response.statusCode == 200 && response.data!['success'] == true) { return ["success"]; - } else { - return ["failed"]; } + return ["failed"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError(e, {}, "SmmsImageUploadUtils", "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/tencent_api.dart b/lib/api/tencent_api.dart index 68d67fd..409041f 100644 --- a/lib/api/tencent_api.dart +++ b/lib/api/tencent_api.dart @@ -2,14 +2,18 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; +import 'package:path/path.dart' as my_path; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; class TencentImageUploadUtils { //表单上传的signature - static String getUploadAuthorization(String secretKey, String keyTime, String uploadPolicyStr) { + static String getUploadAuthorization( + String secretKey, + String keyTime, + String uploadPolicyStr, + ) { String signKey = Hmac(sha1, utf8.encode(secretKey)).convert(utf8.encode(keyTime)).toString(); String stringtosign = sha1.convert(utf8.encode(uploadPolicyStr)).toString(); String signature = Hmac(sha1, utf8.encode(signKey)).convert(utf8.encode(stringtosign)).toString(); @@ -44,139 +48,135 @@ class TencentImageUploadUtils { } //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String bucket = configMap['bucket']; - String area = configMap['area']; - String tencentpath = configMap['path']; - String customUrl = configMap['customUrl']; - String options = configMap['options']; + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { + try { + String secretId = configMap['secretId'] ?? ''; + String secretKey = configMap['secretKey'] ?? ''; + String bucket = configMap['bucket'] ?? ''; + String area = configMap['area'] ?? ''; + String tencentpath = configMap['path'] ?? ''; + String customUrl = configMap['customUrl'] ?? ''; + String options = configMap['options'] ?? ''; - if (customUrl != "None") { - if (!customUrl.startsWith(RegExp(r'http(s)?://'))) { - customUrl = 'http://$customUrl'; + if (customUrl != "None") { + if (!customUrl.startsWith(RegExp(r'http(s)?://'))) { + customUrl = 'http://$customUrl'; + } } - } - if (tencentpath != 'None') { - tencentpath = '${tencentpath.replaceAll(RegExp(r'^/*'), '').replaceAll(RegExp(r'/*$'), '')}/'; - } - String host = '$bucket.cos.$area.myqcloud.com'; - //云存储的路径 - String urlpath = ''; - if (tencentpath != 'None') { - urlpath = '/$tencentpath$name'; - } else { - urlpath = '/$name'; - } - int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; - int endTimestamp = startTimestamp + 86400; - String keyTime = '$startTimestamp;$endTimestamp'; - Map uploadPolicy = { - "expiration": "2033-03-03T09:38:12.414Z", - "conditions": [ - {"acl": "default"}, - {"bucket": bucket}, - {"key": urlpath}, - {"q-sign-algorithm": "sha1"}, - {"q-ak": secretId}, - {"q-sign-time": keyTime} - ] - }; - String uploadPolicyStr = jsonEncode(uploadPolicy); - String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'policy': base64Encode(utf8.encode(uploadPolicyStr)), - 'acl': 'default', - 'q-sign-algorithm': 'sha1', - 'q-ak': secretId, - 'q-key-time': keyTime, - 'q-sign-time': keyTime, - 'q-signature': singature, - 'file': await MultipartFile.fromFile(path, filename: name), - }); - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(path); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); + if (tencentpath != 'None') { + tencentpath = '${tencentpath.replaceAll(RegExp(r'^/*'), '').replaceAll(RegExp(r'/*$'), '')}/'; + } + String host = '$bucket.cos.$area.myqcloud.com'; + //云存储的路径 + String urlpath = ''; + if (tencentpath != 'None') { + urlpath = '/$tencentpath$name'; + } else { + urlpath = '/$name'; + } + int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; + int endTimestamp = startTimestamp + 86400; + String keyTime = '$startTimestamp;$endTimestamp'; + Map uploadPolicy = { + "expiration": "2033-03-03T09:38:12.414Z", + "conditions": [ + {"acl": "default"}, + {"bucket": bucket}, + {"key": urlpath}, + {"q-sign-algorithm": "sha1"}, + {"q-ak": secretId}, + {"q-sign-time": keyTime} + ] + }; + String uploadPolicyStr = jsonEncode(uploadPolicy); + String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); + FormData formData = FormData.fromMap({ + 'key': urlpath, + 'policy': base64Encode(utf8.encode(uploadPolicyStr)), + 'acl': 'default', + 'q-sign-algorithm': 'sha1', + 'q-ak': secretId, + 'q-key-time': keyTime, + 'q-sign-time': keyTime, + 'q-signature': singature, + 'file': await MultipartFile.fromFile(path, filename: my_path.basename(name)), + }); + BaseOptions baseoptions = setBaseOptions(); + File uploadFile = File(path); + String contentLength = await uploadFile.length().then((value) { + return value.toString(); + }); + baseoptions.headers = { + 'Host': host, + 'Content-Type': Global.multipartString, + 'Content-Length': contentLength, + }; + Dio dio = Dio(baseoptions); - try { var response = await dio.post( 'https://$host', data: formData, + onSendProgress: onSendProgress, + cancelToken: cancelToken, ); + if (response.statusCode != 204) { + return ['failed']; + } - if (response.statusCode == 204) { - String returnUrl = ''; - String displayUrl = ''; + String returnUrl = ''; + String displayUrl = ''; - if (customUrl != 'None') { - if (!customUrl.endsWith('/')) { - returnUrl = '$customUrl$urlpath'; - displayUrl = '$customUrl$urlpath'; - } else { - customUrl = customUrl.substring(0, customUrl.length - 1); - returnUrl = '$customUrl$urlpath'; - displayUrl = '$customUrl$urlpath'; - } + if (customUrl != 'None') { + if (!customUrl.endsWith('/')) { + returnUrl = '$customUrl$urlpath'; + displayUrl = '$customUrl$urlpath'; } else { - returnUrl = 'https://$host$urlpath'; - displayUrl = 'https://$host$urlpath'; + customUrl = customUrl.substring(0, customUrl.length - 1); + returnUrl = '$customUrl$urlpath'; + displayUrl = '$customUrl$urlpath'; } + } else { + returnUrl = 'https://$host$urlpath'; + displayUrl = 'https://$host$urlpath'; + } - if (options == 'None') { - displayUrl = "$displayUrl?imageMogr2/thumbnail/500x500"; - } else { - //网站后缀以?开头 - if (!options.startsWith('?')) { - options = '?$options'; - } - returnUrl = '$returnUrl$options'; - displayUrl = '$displayUrl$options'; + if (options == 'None') { + displayUrl = "$displayUrl?imageMogr2/thumbnail/500x500"; + } else { + //网站后缀以?开头 + if (!options.startsWith('?')) { + options = '?$options'; } + returnUrl = '$returnUrl$options'; + displayUrl = '$displayUrl$options'; + } - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, name); - } else { - formatedURL = returnUrl; - } - Map pictureKeyMap = Map.from(configMap); - String pictureKey = jsonEncode(pictureKeyMap); - return ["success", formatedURL, returnUrl, pictureKey, displayUrl]; + String formatedURL = ''; + if (Global.isCopyLink == true) { + formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, name); } else { - return ['failed']; + formatedURL = returnUrl; } + Map pictureKeyMap = Map.from(configMap); + String pictureKey = jsonEncode(pictureKeyMap); + return ["success", formatedURL, returnUrl, pictureKey, displayUrl]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "TencentImageUploadUtils", + "uploadApi"); + return ['failed']; } } @@ -224,32 +224,19 @@ class TencentImageUploadUtils { var response = await dio.delete( deleteHost, ); - if (response.statusCode == 204) { - return [ - "success", - ]; - } else { - return ["failed"]; + if (response.statusCode != 204) { + return ['failed']; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'fileName': fileName, + }, + "TencentImageUploadUtils", + "deleteApi"); + return ['failed']; } } } diff --git a/lib/api/upyun_api.dart b/lib/api/upyun_api.dart index 8217d4a..57ac92f 100644 --- a/lib/api/upyun_api.dart +++ b/lib/api/upyun_api.dart @@ -2,140 +2,141 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; +import 'package:path/path.dart' as my_path; import 'package:horopic/utils/common_functions.dart'; import 'package:horopic/utils/global.dart'; +import 'package:horopic/utils/picbed/upyun.dart'; class UpyunImageUploadUtils { //上传接口 - static uploadApi({required String path, required String name, required Map configMap}) async { - String bucket = configMap['bucket']; - String upyunOperator = configMap['operator']; - String password = configMap['password']; - String url = configMap['url']; - String options = configMap['options']; - String upyunpath = configMap['path']; - if (options == ' ' || options.trim() == '') { - options = ''; - } + static uploadApi({ + required String path, + required String name, + required Map configMap, + Function(int, int)? onSendProgress, + CancelToken? cancelToken, + }) async { + try { + String bucket = configMap['bucket'] ?? ''; + String upyunOperator = configMap['operator'] ?? ''; + String password = configMap['password'] ?? ''; + String url = configMap['url'] ?? ''; + String options = configMap['options'] ?? ''; + String upyunpath = configMap['path'] ?? ''; + String antiLeechToken = configMap['antiLeechToken'] ?? ''; + String antiLeechExpiration = configMap['antiLeechExpiration'] ?? ''; + if (options.trim() == '') { + options = ''; + } - //格式化 - if (url != "None") { - if (!url.startsWith(RegExp(r'http(s)?://'))) { - url = 'http://$url'; + if (url != "None") { + if (!url.startsWith(RegExp(r'http(s)?://'))) { + url = 'http://$url'; + } } - } - //格式化 - if (upyunpath != 'None') { - if (upyunpath.startsWith('/')) { - upyunpath = upyunpath.substring(1); + if (upyunpath == '/' || upyunpath == '') { + upyunpath = 'None'; } - if (!upyunpath.endsWith('/')) { - upyunpath = '$upyunpath/'; + if (upyunpath != 'None') { + if (upyunpath.startsWith('/')) { + upyunpath = upyunpath.substring(1); + } + if (!upyunpath.endsWith('/')) { + upyunpath = '$upyunpath/'; + } } - } - String host = 'http://v0.api.upyun.com'; - //云存储的路径 - String urlpath = ''; - if (upyunpath != 'None') { - urlpath = '/$upyunpath$name'; - } else { - urlpath = '/$name'; - } - String date = HttpDate.format(DateTime.now()); - File uploadFile = File(path); - String uploadFileMd5 = await uploadFile.readAsBytes().then((value) { - return md5.convert(value).toString(); - }); - Map uploadPolicy = { - 'bucket': bucket, - 'save-key': urlpath, - 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, - 'date': date, - 'content-md5': uploadFileMd5, - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String stringToSign = 'POST&/$bucket&$date&$base64Policy&$uploadFileMd5'; - String passwordMd5 = md5.convert(utf8.encode(password)).toString(); - String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); - String authorization = 'UPYUN $upyunOperator:$signature'; - FormData formData = FormData.fromMap({ - 'authorization': authorization, - 'policy': base64Policy, - 'file': await MultipartFile.fromFile(path, filename: name), - }); - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': 'v0.api.upyun.com', - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - 'Date': date, - 'Authorization': authorization, - 'Content-MD5': uploadFileMd5, - }; - Dio dio = Dio(baseoptions); - try { + String host = 'http://v0.api.upyun.com'; + //云存储的路径 + String urlpath = ''; + if (upyunpath != 'None') { + urlpath = '/$upyunpath$name'; + } else { + urlpath = '/$name'; + } + String date = HttpDate.format(DateTime.now()); + File uploadFile = File(path); + String uploadFileMd5 = await uploadFile.readAsBytes().then((value) { + return md5.convert(value).toString(); + }); + String base64Policy = + getUpyunUploadPolicy(bucket: bucket, saveKey: urlpath, contentMd5: uploadFileMd5, date: date); + + String authorization = getUpyunUploadAuthHeader( + bucket: bucket, + saveKey: urlpath, + contentMd5: uploadFileMd5, + operator: upyunOperator, + password: password, + base64Policy: base64Policy, + date: date); + FormData formData = FormData.fromMap({ + 'authorization': authorization, + 'policy': base64Policy, + 'file': await MultipartFile.fromFile(path, filename: my_path.basename(path)), + }); + BaseOptions baseoptions = setBaseOptions(); + String contentLength = await uploadFile.length().then((value) { + return value.toString(); + }); + baseoptions.headers = { + 'Host': 'v0.api.upyun.com', + 'Content-Type': Global.multipartString, + 'Content-Length': contentLength, + 'Date': date, + 'Authorization': authorization, + 'Content-MD5': uploadFileMd5, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( '$host/$bucket', data: formData, + onSendProgress: onSendProgress, + cancelToken: cancelToken, ); + if (response.statusCode != 200) { + return ['failed']; + } + String returnUrl = ''; + String displayUrl = ''; + String antiLeechQuery = getUpyunAntiLeechParam( + saveKey: urlpath, antiLeechToken: antiLeechToken, antiLeechExpiration: antiLeechExpiration); + if (urlpath.startsWith('/')) { + urlpath = urlpath.substring(1); + } + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); + } - if (response.statusCode == 200) { - String returnUrl = ''; - String displayUrl = ''; - if (urlpath.startsWith('/')) { - urlpath = urlpath.substring(1); - } - - if (!url.endsWith('/')) { - returnUrl = '$url/$urlpath'; - displayUrl = '$url/$urlpath'; - } else { - url = url.substring(0, url.length - 1); - returnUrl = '$url/$urlpath'; - displayUrl = '$url/$urlpath'; - } - - returnUrl = '$returnUrl$options'; - displayUrl = '$displayUrl$options'; - - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, name); + returnUrl = '$url/$urlpath$options'; + if (antiLeechQuery != '') { + if (options != '') { + returnUrl = '$url/$urlpath$options&$antiLeechQuery'; } else { - formatedURL = returnUrl; + returnUrl = '$url/$urlpath?$antiLeechQuery'; } - Map pictureKeyMap = Map.from(configMap); - String pictureKey = jsonEncode(pictureKeyMap); - return ["success", formatedURL, returnUrl, pictureKey, displayUrl]; - } else { - return ['failed']; } - } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); + displayUrl = returnUrl; + String formatedURL = ''; + if (Global.isCopyLink == true) { + formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, my_path.basename(path)); } else { - FLog.error( - className: "UpyunImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); + formatedURL = returnUrl; } - return [e.toString()]; + Map pictureKeyMap = Map.from(configMap); + String pictureKey = jsonEncode(pictureKeyMap); + return ["success", formatedURL, returnUrl, pictureKey, displayUrl]; + } catch (e) { + flogError( + e, + { + 'path': path, + 'name': name, + }, + "UpyunImageUploadUtils", + "uploadApi"); + return ["failed"]; } } @@ -183,32 +184,19 @@ class UpyunImageUploadUtils { var response = await dio.delete( deleteHost, ); - if (response.statusCode == 200) { - return [ - "success", - ]; - } else { + if (response.statusCode != 200) { return ["failed"]; } + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({ - 'fileName': fileName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'fileName': fileName, + }, + "UpyunImageUploadUtils", + "deleteApi"); + return ["failed"]; } } } diff --git a/lib/api/webdav_api.dart b/lib/api/webdav_api.dart index dd8b3bb..2c0beb8 100644 --- a/lib/api/webdav_api.dart +++ b/lib/api/webdav_api.dart @@ -1,6 +1,4 @@ import 'dart:convert'; -import 'package:dio/dio.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:webdav_client/webdav_client.dart' as webdav; import 'package:horopic/utils/common_functions.dart'; @@ -10,28 +8,28 @@ import 'package:horopic/picture_host_manage/manage_api/webdav_manage_api.dart'; class WebdavImageUploadUtils { //上传接口 static uploadApi({required String path, required String name, required Map configMap}) async { - String formatedURL = ''; - webdav.Client client = await WebdavManageAPI.getWebdavClient(); - String uploadPath = configMap['uploadPath']; - String? customUrl = configMap['customUrl']; - String? webPath = configMap['webPath']; + try { + String formatedURL = ''; + webdav.Client client = await WebdavManageAPI.getWebdavClient(); + String uploadPath = configMap['uploadPath']; + String? customUrl = configMap['customUrl']; + String? webPath = configMap['webPath']; - customUrl ??= 'None'; - webPath ??= 'None'; + customUrl ??= 'None'; + webPath ??= 'None'; - if (uploadPath == 'None') { - uploadPath = '/'; - } else { - if (!uploadPath.startsWith('/')) { - uploadPath = '/$uploadPath'; - } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; + if (uploadPath == 'None') { + uploadPath = '/'; + } else { + if (!uploadPath.startsWith('/')) { + uploadPath = '/$uploadPath'; + } + if (!uploadPath.endsWith('/')) { + uploadPath = '$uploadPath/'; + } } - } - String filePath = uploadPath + name; + String filePath = uploadPath + name; - try { await client.writeFromFile(path, filePath); String returnUrl = ''; @@ -67,36 +65,27 @@ class WebdavImageUploadUtils { displayUrl, ]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavImageUploadUtils", - methodName: "uploadApi", - text: formatErrorMessage({ - 'path': path, - 'name': name, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError( + e, + { + 'path': path, + 'name': name, + }, + "WebdavImageUploadUtils", + "uploadApi"); + + return ["failed"]; } } static deleteApi({required Map deleteMap, required Map configMap}) async { - Map configMapFromPictureKey = jsonDecode(deleteMap['pictureKey']); - String host = configMapFromPictureKey['host']; - String webdavusername = configMapFromPictureKey['webdavusername']; - String password = configMapFromPictureKey['password']; - try { + Map configMapFromPictureKey = jsonDecode(deleteMap['pictureKey']); + + String host = configMapFromPictureKey['host']; + String webdavusername = configMapFromPictureKey['webdavusername']; + String password = configMapFromPictureKey['password']; + webdav.Client client = webdav.newClient( host, user: webdavusername, @@ -108,24 +97,10 @@ class WebdavImageUploadUtils { client.setReceiveTimeout(30000); await client.remove(configMapFromPictureKey['pictureKey']); - return [ - "success", - ]; + return ["success"]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavImageUploadUtils", - methodName: "deleteApi", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } - return [e.toString()]; + flogError(e, {}, "WebdavImageUploadUtils", "deleteApi"); + return ["failed"]; } } } diff --git a/lib/configure_page/common_configure/rename_uploaded_file.dart b/lib/configure_page/common_configure/rename_uploaded_file.dart index ce43966..3d720bb 100644 --- a/lib/configure_page/common_configure/rename_uploaded_file.dart +++ b/lib/configure_page/common_configure/rename_uploaded_file.dart @@ -15,7 +15,7 @@ class RenameFileState extends State { '{y}': "两位数年份(22)", '{m}': "月份(01-12)", '{d}': "日期(01-31)", - '{timestamp}': "时间戳(秒)", + '{timestamp}': "时间戳(毫秒)", '{uuid}': "唯一字符串", '{md5}': "随机md5", '{md5-16}': "随机md5前16位", diff --git a/lib/configure_page/common_configure/select_default_picture_host.dart b/lib/configure_page/common_configure/select_default_picture_host.dart index 4b3e518..f0ddc9b 100644 --- a/lib/configure_page/common_configure/select_default_picture_host.dart +++ b/lib/configure_page/common_configure/select_default_picture_host.dart @@ -2,27 +2,20 @@ import 'dart:convert'; import 'dart:io'; import 'package:barcode_scan2/barcode_scan2.dart'; -import 'package:crypto/crypto.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluro/fluro.dart'; import 'package:f_logs/f_logs.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:qiniu_flutter_sdk/qiniu_flutter_sdk.dart'; -import 'package:path/path.dart' as mypath; import 'package:horopic/utils/global.dart'; import 'package:horopic/utils/common_functions.dart'; -import 'package:horopic/utils/dio_proxy_adapter.dart'; import 'package:horopic/picture_host_configure/configure_page/configure_export.dart'; import 'package:horopic/pages/loading.dart'; -import 'package:horopic/api/qiniu_api.dart'; -import 'package:horopic/api/tencent_api.dart'; import 'package:horopic/router/application.dart'; import 'package:horopic/router/routers.dart'; @@ -70,65 +63,65 @@ class AllPShostState extends State { } } - //smms配置 static Future get localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } + //smms配置 Future get smmsFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_smms_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_smms_config.txt')); } //lskypro配置 Future get lskyFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_host_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_host_config.txt')); } //github配置 Future get githubFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_github_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_github_config.txt')); } //imgur配置 Future get imgurFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_imgur_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_imgur_config.txt')); } //qiniu配置 Future get qiniuFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_qiniu_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_qiniu_config.txt')); } //tencent配置 Future get tencentFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_tencent_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_tencent_config.txt')); } //aliyun配置 Future get aliyunFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_aliyun_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_aliyun_config.txt')); } //upyun配置 Future get upyunFile async { final path = await localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_upyun_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_upyun_config.txt')); } exportConfiguration(String pshost) async { @@ -150,9 +143,13 @@ class AllPShostState extends State { "webdav": "$configPath/${defaultUser}_webdav_config.txt", }; String config = await File(configFilePath[pshost]!).readAsString(); + if (config == '') { + return Fluttertoast.showToast( + msg: "该图床未配置", toastLength: Toast.LENGTH_SHORT, timeInSecForIosWeb: 2, fontSize: 16.0); + } Map configMap = jsonDecode(config); - Map configMap2 = {pshost: configMap}; - String configJson = jsonEncode(configMap2); + Map configMapWithPshost = {pshost: configMap}; + String configJson = jsonEncode(configMapWithPshost); configJson = configJson.replaceAll('None', ''); configJson = configJson.replaceAll('keyId', 'accessKeyId'); configJson = configJson.replaceAll('keySecret', 'accessKeySecret'); @@ -194,6 +191,9 @@ class AllPShostState extends State { continue; } String config = await File(configFilePath[key]!).readAsString(); + if (config == '') { + continue; + } Map configMap2 = jsonDecode(config); configMap[key] = configMap2; } @@ -230,36 +230,13 @@ class AllPShostState extends State { Map jsonResult = jsonDecode(result); if (jsonResult['smms'] != null) { - final smmsToken = jsonResult['smms']['token']; + final smmsToken = jsonResult['smms']['token'] ?? ''; try { - String validateURL = "https://smms.app/api/v2/profile"; - BaseOptions options = setBaseOptions(); - options.headers = { - "Content-Type": 'multipart/form-data', - "Authorization": smmsToken, - }; - //需要加一个空的formdata,不然会报错 - FormData formData = FormData.fromMap({}); - Dio dio = Dio(options); - try { - var validateResponse = await dio.post(validateURL, data: formData); - if (validateResponse.statusCode == 200 && validateResponse.data['success'] == true) { - final smmsConfig = SmmsConfigModel(smmsToken); - final smmsConfigJson = jsonEncode(smmsConfig); - final smmsConfigFile = await smmsFile; - await smmsConfigFile.writeAsString(smmsConfigJson); - showToast("sm.ms配置成功"); - } else { - showToast("sm.ms验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_smms_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - rethrow; - } + final smmsConfig = SmmsConfigModel(smmsToken); + final smmsConfigJson = jsonEncode(smmsConfig); + final smmsConfigFile = await smmsFile; + await smmsConfigFile.writeAsString(smmsConfigJson); + showToast("sm.ms配置成功"); } catch (e) { FLog.error( className: 'AllPShostState', @@ -272,22 +249,21 @@ class AllPShostState extends State { if (jsonResult['github'] != null) { try { - String token = jsonResult['github']['token']; - String githubUserApi = 'https://api.github.com/user'; - String usernameRepo = jsonResult['github']['repo']; + String token = jsonResult['github']['token'] ?? ''; + String usernameRepo = jsonResult['github']['repo'] ?? ''; String githubusername = usernameRepo.substring(0, usernameRepo.indexOf('/')); String repo = usernameRepo.substring(usernameRepo.indexOf('/') + 1); - String storePath = jsonResult['github']['path']; - if (storePath == '' || storePath.isEmpty) { + String storePath = jsonResult['github']['path'] ?? ''; + if (storePath == '' || storePath.isEmpty || storePath == '/') { storePath = 'None'; } else if (!storePath.endsWith('/')) { storePath = '$storePath/'; } - String branch = jsonResult['github']['branch']; + String branch = jsonResult['github']['branch'] ?? ''; if (branch == '' || branch.isEmpty) { branch = 'main'; } - String customDomain = jsonResult['github']['customUrl']; + String customDomain = jsonResult['github']['customUrl'] ?? ''; if (customDomain == '' || customDomain.isEmpty) { customDomain = 'None'; } @@ -300,46 +276,16 @@ class AllPShostState extends State { } } token = token.startsWith('Bearer ') ? token : 'Bearer $token'; - try { - BaseOptions options = setBaseOptions(); - options.headers = { - "Accept": 'application/vnd.github+json', - "Authorization": token, - }; - //需要加一个空的formdata,不然会报错 - Map queryData = {}; - Dio dio = Dio(options); - try { - var validateResponse = await dio.get(githubUserApi, queryParameters: queryData); - if (validateResponse.statusCode == 200 && validateResponse.data.toString().contains("email")) { - final githubConfig = GithubConfigModel(githubusername, repo, token, storePath, branch, customDomain); - final githubConfigJson = jsonEncode(githubConfig); - final githubConfigFile = await githubFile; - await githubConfigFile.writeAsString(githubConfigJson); - showToast("Github配置成功"); - } else { - showToast("Github验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_github_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - showToast("Github验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_github_2', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - showToast("Github配置错误"); - } + + final githubConfig = GithubConfigModel(githubusername, repo, token, storePath, branch, customDomain); + final githubConfigJson = jsonEncode(githubConfig); + final githubConfigFile = await githubFile; + await githubConfigFile.writeAsString(githubConfigJson); + showToast("Github配置成功"); } catch (e) { FLog.error( className: 'AllPShostState', - methodName: 'processingQRCodeResult_github_3', + methodName: 'processingQRCodeResult_github', text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); showToast("Github配置错误"); @@ -349,57 +295,32 @@ class AllPShostState extends State { if (jsonResult['lankong'] != null) { try { String lankongVersion = jsonResult['lankong']['lskyProVersion']; - if (lankongVersion == 'V2') { - String lankongVtwoHost = jsonResult['lankong']['server']; - if (lankongVtwoHost.endsWith('/')) { - lankongVtwoHost = lankongVtwoHost.substring(0, lankongVtwoHost.length - 1); - } - String lankongToken = jsonResult['lankong']['token']; - if (lankongToken.startsWith('Bearer ')) { - } else { - lankongToken = 'Bearer $lankongToken'; - } - String lanKongstrategyId = jsonResult['lankong']['strategyId']; - if (lanKongstrategyId == '' || lanKongstrategyId.isEmpty) { - lanKongstrategyId = 'None'; - } - String lanKongalbumId = jsonResult['lankong']['albumId']; - if (lanKongalbumId == '' || lanKongalbumId.isEmpty) { - lanKongalbumId = 'None'; - } - - BaseOptions options = setBaseOptions(); - options.headers = { - "Accept": "application/json", - "Authorization": lankongToken, - }; - String profileUrl = "$lankongVtwoHost/api/v1/profile"; - Dio dio = Dio(options); - try { - var response = await dio.get( - profileUrl, - ); - if (response.statusCode == 200 && response.data['status'] == true) { - HostConfigModel hostConfig = - HostConfigModel(lankongVtwoHost, lankongToken, lanKongstrategyId, lanKongalbumId); - final hostConfigJson = jsonEncode(hostConfig); - final hostConfigFile = await lskyFile; - hostConfigFile.writeAsString(hostConfigJson); - showToast("兰空配置成功"); - } else { - showToast("兰空验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_lankong_2', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - showToast("兰空配置错误"); - } - } else { + if (lankongVersion == 'V1') { showToast("不支持兰空V1"); } + String lankongVtwoHost = jsonResult['lankong']['server']; + if (lankongVtwoHost.endsWith('/')) { + lankongVtwoHost = lankongVtwoHost.substring(0, lankongVtwoHost.length - 1); + } + String lankongToken = jsonResult['lankong']['token']; + if (lankongToken.startsWith('Bearer ')) { + } else { + lankongToken = 'Bearer $lankongToken'; + } + String lanKongstrategyId = jsonResult['lankong']['strategyId']; + if (lanKongstrategyId == '' || lanKongstrategyId.isEmpty) { + lanKongstrategyId = 'None'; + } + String lanKongalbumId = jsonResult['lankong']['albumId']; + if (lanKongalbumId == '' || lanKongalbumId.isEmpty) { + lanKongalbumId = 'None'; + } + + HostConfigModel hostConfig = HostConfigModel(lankongVtwoHost, lankongToken, lanKongstrategyId, lanKongalbumId); + final hostConfigJson = jsonEncode(hostConfig); + final hostConfigFile = await lskyFile; + hostConfigFile.writeAsString(hostConfigJson); + showToast("兰空配置成功"); } catch (e) { FLog.error( className: 'AllPShostState', @@ -411,55 +332,17 @@ class AllPShostState extends State { } if (jsonResult['imgur'] != null) { - final imgurclientId = jsonResult['imgur']['clientId']; - String imgurProxy = jsonResult['imgur']['proxy']; + final imgurclientId = jsonResult['imgur']['clientId'] ?? ''; + String imgurProxy = jsonResult['imgur']['proxy'] ?? ''; if (imgurProxy.isEmpty) { imgurProxy = 'None'; } try { - String baiduPicUrl = - "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white-d0c9fe2af5.png"; - String validateURL = "https://api.imgur.com/3/image"; - - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": "Client-ID $imgurclientId", - }; - //需要加一个空的formdata,不然会报错 - FormData formData = FormData.fromMap({ - "image": baiduPicUrl, - }); - Dio dio = Dio(options); - String proxyClean = ''; - - if (imgurProxy != 'None') { - if (imgurProxy.startsWith('http://') || imgurProxy.startsWith('https://')) { - proxyClean = imgurProxy.split('://')[1]; - } else { - proxyClean = imgurProxy; - } - dio.httpClientAdapter = useProxy(proxyClean); - } - - try { - var validateResponse = await dio.post(validateURL, data: formData); - if (validateResponse.statusCode == 200 && validateResponse.data['success'] == true) { - final imgurConfig = ImgurConfigModel(imgurclientId, imgurProxy); - final imgurConfigJson = jsonEncode(imgurConfig); - final imgurConfigFile = await smmsFile; - await imgurConfigFile.writeAsString(imgurConfigJson); - showToast("Imgur配置成功"); - } else { - showToast("Imgur验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_imgur_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - rethrow; - } + final imgurConfig = ImgurConfigModel(imgurclientId, imgurProxy); + final imgurConfigJson = jsonEncode(imgurConfig); + final imgurConfigFile = await smmsFile; + await imgurConfigFile.writeAsString(imgurConfigJson); + showToast("Imgur配置成功"); } catch (e) { FLog.error( className: 'AllPShostState', @@ -471,13 +354,13 @@ class AllPShostState extends State { } if (jsonResult['qiniu'] != null) { - String qiniuAccessKey = jsonResult['qiniu']['accessKey']; - String qiniuSecretKey = jsonResult['qiniu']['secretKey']; - String qiniuBucket = jsonResult['qiniu']['bucket']; - String qiniuUrl = jsonResult['qiniu']['url']; - String qiniuArea = jsonResult['qiniu']['area']; - String qiniuOptions = jsonResult['qiniu']['options']; - String qiniuPath = jsonResult['qiniu']['path']; + String qiniuAccessKey = jsonResult['qiniu']['accessKey'] ?? ''; + String qiniuSecretKey = jsonResult['qiniu']['secretKey'] ?? ''; + String qiniuBucket = jsonResult['qiniu']['bucket'] ?? ''; + String qiniuUrl = jsonResult['qiniu']['url'] ?? ''; + String qiniuArea = jsonResult['qiniu']['area'] ?? ''; + String qiniuOptions = jsonResult['qiniu']['options'] ?? ''; + String qiniuPath = jsonResult['qiniu']['path'] ?? ''; try { if (!qiniuUrl.startsWith('http') && !qiniuUrl.startsWith('https')) { @@ -500,53 +383,16 @@ class AllPShostState extends State { if (qiniuOptions.isEmpty) { qiniuOptions = 'None'; - } else { - if (!qiniuOptions.startsWith('?')) { - qiniuOptions = '?$qiniuOptions'; - } - } - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); + } else if (!qiniuOptions.startsWith('?')) { + qiniuOptions = '?$qiniuOptions'; } - String key = 'PicHoroValidate.jpeg'; - String urlSafeBase64EncodePutPolicy = - QiniuImageUploadUtils.geturlSafeBase64EncodePutPolicy(qiniuBucket, key, qiniuPath); - String uploadToken = - QiniuImageUploadUtils.getUploadToken(qiniuAccessKey, qiniuSecretKey, urlSafeBase64EncodePutPolicy); - Storage storage = Storage( - config: Config( - retryLimit: 5, - )); - try { - PutResponse putresult = await storage.putFile(File(assetFilePath), uploadToken); - if (putresult.key == key || putresult.key == '$qiniuPath$key') { - final qiniuConfig = QiniuConfigModel( - qiniuAccessKey, qiniuSecretKey, qiniuBucket, qiniuUrl, qiniuArea, qiniuOptions, qiniuPath); - final qiniuConfigJson = jsonEncode(qiniuConfig); - final qiniuConfigFile = await qiniuFile; - await qiniuConfigFile.writeAsString(qiniuConfigJson); - showToast("七牛配置成功"); - } else { - showToast("七牛验证失败"); - } - } catch (e) { - FLog.error( - className: 'AllPShostState', - methodName: 'processingQRCodeResult_qiniu_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - rethrow; - } + final qiniuConfig = + QiniuConfigModel(qiniuAccessKey, qiniuSecretKey, qiniuBucket, qiniuUrl, qiniuArea, qiniuOptions, qiniuPath); + final qiniuConfigJson = jsonEncode(qiniuConfig); + final qiniuConfigFile = await qiniuFile; + await qiniuConfigFile.writeAsString(qiniuConfigJson); + showToast("七牛配置成功"); } catch (e) { FLog.error( className: 'AllPShostState', @@ -559,150 +405,79 @@ class AllPShostState extends State { if (jsonResult['tcyun'] != null) { String tencentVersion = jsonResult['tcyun']['version']; - if (tencentVersion == 'v5') { - String tencentSecretId = jsonResult['tcyun']['secretId']; - String tencentSecretKey = jsonResult['tcyun']['secretKey']; - String tencentBucket = jsonResult['tcyun']['bucket']; - String tencentAppId = jsonResult['tcyun']['appId']; - String tencentArea = jsonResult['tcyun']['area']; - String tencentPath = jsonResult['tcyun']['path']; - String tencentCustomUrl = jsonResult['tcyun']['customUrl']; - String tencentOptions = jsonResult['tcyun']['options']; - - try { - if (tencentCustomUrl.isNotEmpty) { - if (!tencentCustomUrl.startsWith('http') && !tencentCustomUrl.startsWith('https')) { - tencentCustomUrl = 'http://$tencentCustomUrl'; - } - if (tencentCustomUrl.endsWith('/')) { - tencentCustomUrl = tencentCustomUrl.substring(0, tencentCustomUrl.length - 1); - } - } else { - tencentCustomUrl = 'None'; - } + if (tencentVersion != 'v5') { + showToast("不支持腾讯V4"); + } + String tencentSecretId = jsonResult['tcyun']['secretId'] ?? ''; + String tencentSecretKey = jsonResult['tcyun']['secretKey'] ?? ''; + String tencentBucket = jsonResult['tcyun']['bucket'] ?? ''; + String tencentAppId = jsonResult['tcyun']['appId'] ?? ''; + String tencentArea = jsonResult['tcyun']['area'] ?? ''; + String tencentPath = jsonResult['tcyun']['path'] ?? ''; + String tencentCustomUrl = jsonResult['tcyun']['customUrl'] ?? ''; + String tencentOptions = jsonResult['tcyun']['options'] ?? ''; - if (tencentPath.isEmpty) { - tencentPath = 'None'; - } else { - if (tencentPath.startsWith('/')) { - tencentPath = tencentPath.substring(1); - } - if (!tencentPath.endsWith('/')) { - tencentPath = '$tencentPath/'; - } + try { + if (tencentCustomUrl.isNotEmpty) { + if (!tencentCustomUrl.startsWith('http') && !tencentCustomUrl.startsWith('https')) { + tencentCustomUrl = 'http://$tencentCustomUrl'; } - - if (tencentOptions.isEmpty) { - tencentOptions = 'None'; - } else { - if (!tencentOptions.startsWith('?')) { - tencentOptions = '?$tencentOptions'; - } + if (tencentCustomUrl.endsWith('/')) { + tencentCustomUrl = tencentCustomUrl.substring(0, tencentCustomUrl.length - 1); } - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); + } else { + tencentCustomUrl = 'None'; + } - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); + if (tencentPath.isEmpty || tencentPath == '/') { + tencentPath = 'None'; + } else { + if (tencentPath.startsWith('/')) { + tencentPath = tencentPath.substring(1); } - String key = 'PicHoroValidate.jpeg'; - String host = '$tencentBucket.cos.$tencentArea.myqcloud.com'; - String urlpath = ''; - if (tencentPath != 'None') { - urlpath = '$tencentPath$key'; - } else { - urlpath = key; + if (!tencentPath.endsWith('/')) { + tencentPath = '$tencentPath/'; } - int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; - int endTimestamp = startTimestamp + 86400; - String keyTime = '$startTimestamp;$endTimestamp'; - Map uploadPolicy = { - "expiration": "2033-03-03T09:38:12.414Z", - "conditions": [ - {"acl": "default"}, - {"bucket": tencentBucket}, - {"key": urlpath}, - {"q-sign-algorithm": "sha1"}, - {"q-ak": tencentSecretId}, - {"q-sign-time": keyTime} - ] - }; - String uploadPolicyStr = jsonEncode(uploadPolicy); - String singature = TencentImageUploadUtils.getUploadAuthorization(tencentSecretKey, keyTime, uploadPolicyStr); - //policy中的字段,除了bucket,其它的都要在formdata中添加 - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'policy': base64Encode(utf8.encode(uploadPolicyStr)), - 'acl': 'default', - 'q-sign-algorithm': 'sha1', - 'q-ak': tencentSecretId, - 'q-key-time': keyTime, - 'q-sign-time': keyTime, - 'q-signature': singature, - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - - var response = await dio.post( - 'http://$host', - data: formData, - ); + } - if (response.statusCode == 204) { - final tencentConfig = TencentConfigModel( - tencentSecretId, - tencentSecretKey, - tencentBucket, - tencentAppId, - tencentArea, - tencentPath, - tencentCustomUrl, - tencentOptions, - ); - final tencentConfigJson = jsonEncode(tencentConfig); - final tencentConfigFile = await tencentFile; - await tencentConfigFile.writeAsString(tencentConfigJson); - showToast("腾讯云配置成功"); - } else { - showToast("腾讯云验证失败"); - } - } catch (e) { - FLog.error( - className: 'TencentConfigPage', - methodName: 'saveTencentConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - showToast("腾讯云配置错误"); + if (tencentOptions.isEmpty) { + tencentOptions = 'None'; + } else if (!tencentOptions.startsWith('?')) { + tencentOptions = '?$tencentOptions'; } - } else { - showToast("不支持腾讯V4"); + + final tencentConfig = TencentConfigModel( + tencentSecretId, + tencentSecretKey, + tencentBucket, + tencentAppId, + tencentArea, + tencentPath, + tencentCustomUrl, + tencentOptions, + ); + final tencentConfigJson = jsonEncode(tencentConfig); + final tencentConfigFile = await tencentFile; + await tencentConfigFile.writeAsString(tencentConfigJson); + showToast("腾讯云配置成功"); + } catch (e) { + FLog.error( + className: 'TencentConfigPage', + methodName: 'saveTencentConfig', + text: formatErrorMessage({}, e.toString()), + dataLogType: DataLogType.ERRORS.toString()); + showToast("腾讯云配置错误"); } } if (jsonResult['aliyun'] != null) { - String aliyunKeyId = jsonResult['aliyun']['accessKeyId']; - String aliyunKeySecret = jsonResult['aliyun']['accessKeySecret']; - String aliyunBucket = jsonResult['aliyun']['bucket']; - String aliyunArea = jsonResult['aliyun']['area']; - String aliyunPath = jsonResult['aliyun']['path']; - String aliyunCustomUrl = jsonResult['aliyun']['customUrl']; - String aliyunOptions = jsonResult['aliyun']['options']; + String aliyunKeyId = jsonResult['aliyun']['accessKeyId'] ?? ''; + String aliyunKeySecret = jsonResult['aliyun']['accessKeySecret'] ?? ''; + String aliyunBucket = jsonResult['aliyun']['bucket'] ?? ''; + String aliyunArea = jsonResult['aliyun']['area'] ?? ''; + String aliyunPath = jsonResult['aliyun']['path'] ?? ''; + String aliyunCustomUrl = jsonResult['aliyun']['customUrl'] ?? ''; + String aliyunOptions = jsonResult['aliyun']['options'] ?? ''; try { if (aliyunCustomUrl.isNotEmpty) { @@ -716,7 +491,7 @@ class AllPShostState extends State { aliyunCustomUrl = 'None'; } - if (aliyunPath.isEmpty) { + if (aliyunPath.isEmpty || aliyunPath == '/') { aliyunPath = 'None'; } else { if (aliyunPath.startsWith('/')) { @@ -729,84 +504,23 @@ class AllPShostState extends State { if (aliyunOptions.isEmpty) { aliyunOptions = 'None'; - } else { - if (!aliyunOptions.startsWith('?')) { - aliyunOptions = '?$aliyunOptions'; - } + } else if (!aliyunOptions.startsWith('?')) { + aliyunOptions = '?$aliyunOptions'; } - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - String host = '$aliyunBucket.$aliyunArea.aliyuncs.com'; - String urlpath = ''; - if (aliyunPath != 'None') { - urlpath = '$aliyunPath$key'; - } else { - urlpath = key; - } - Map uploadPolicy = { - "expiration": "2034-12-01T12:00:00.000Z", - "conditions": [ - {"bucket": aliyunBucket}, - ["content-length-range", 0, 104857600], - {"key": urlpath} - ] - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String singature = - base64.encode(Hmac(sha1, utf8.encode(aliyunKeySecret)).convert(utf8.encode(base64Policy)).bytes); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'OSSAccessKeyId': aliyunKeyId, - 'policy': base64Policy, - 'Signature': singature, - //阿里默认的content-type是application/octet-stream,这里改成image/xxx - 'x-oss-content-type': 'image/${mypath.extension(assetFilePath).replaceFirst('.', '')}', - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - 'https://$host', - data: formData, + final aliyunConfig = AliyunConfigModel( + aliyunKeyId, + aliyunKeySecret, + aliyunBucket, + aliyunArea, + aliyunPath, + aliyunCustomUrl, + aliyunOptions, ); - - if (response.statusCode == 204) { - final aliyunConfig = AliyunConfigModel( - aliyunKeyId, - aliyunKeySecret, - aliyunBucket, - aliyunArea, - aliyunPath, - aliyunCustomUrl, - aliyunOptions, - ); - final aliyunConfigJson = jsonEncode(aliyunConfig); - final aliyunConfigFile = await aliyunFile; - await aliyunConfigFile.writeAsString(aliyunConfigJson); - showToast("阿里云配置成功"); - } else { - showToast("阿里云验证失败"); - } + final aliyunConfigJson = jsonEncode(aliyunConfig); + final aliyunConfigFile = await aliyunFile; + await aliyunConfigFile.writeAsString(aliyunConfigJson); + showToast("阿里云配置成功"); } catch (e) { FLog.error( className: 'AliyunConfigPage', @@ -818,12 +532,14 @@ class AllPShostState extends State { } if (jsonResult['upyun'] != null) { - String upyunBucket = jsonResult['upyun']['bucket']; - String upyunOperator = jsonResult['upyun']['operator']; - String upyunPassword = jsonResult['upyun']['password']; - String upyunUrl = jsonResult['upyun']['url']; - String upyunOptions = jsonResult['upyun']['options']; - String upyunPath = jsonResult['upyun']['path']; + String upyunBucket = jsonResult['upyun']['bucket'] ?? ''; + String upyunOperator = jsonResult['upyun']['operator'] ?? ''; + String upyunPassword = jsonResult['upyun']['password'] ?? ''; + String upyunUrl = jsonResult['upyun']['url'] ?? ''; + String upyunOptions = jsonResult['upyun']['options'] ?? ''; + String upyunPath = jsonResult['upyun']['path'] ?? ''; + String upyunAntiLeechToken = jsonResult['upyun']['antiLeechToken'] ?? ''; + String upyunAntiLeechExpiration = jsonResult['upyun']['antiLeechExpiration'] ?? ''; try { if (!upyunUrl.startsWith('http') && !upyunUrl.startsWith('https')) { upyunUrl = 'http://$upyunUrl'; @@ -833,7 +549,7 @@ class AllPShostState extends State { upyunUrl = upyunUrl.substring(0, upyunUrl.length - 1); } - if (upyunPath.isEmpty) { + if (upyunPath.isEmpty || upyunPath == '/') { upyunPath = 'None'; } else { if (upyunPath.startsWith('/')) { @@ -844,84 +560,21 @@ class AllPShostState extends State { upyunPath = '$upyunPath/'; } } - //save asset image to app dir - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - String host = 'http://v0.api.upyun.com'; - String urlpath = ''; - if (upyunPath != 'None') { - urlpath = '/$upyunPath$key'; - } else { - urlpath = '/$key'; - } - String date = HttpDate.format(DateTime.now()); - String assetFileMd5 = await assetFile.readAsBytes().then((value) { - return md5.convert(value).toString(); - }); - Map uploadPolicy = { - 'bucket': upyunBucket, - 'save-key': urlpath, - 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, - 'date': date, - 'content-md5': assetFileMd5, - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String stringToSign = 'POST&/$upyunBucket&$date&$base64Policy&$assetFileMd5'; - String passwordMd5 = md5.convert(utf8.encode(upyunPassword)).toString(); - String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); - String authorization = 'UPYUN $upyunOperator:$signature'; - FormData formData = FormData.fromMap({ - 'authorization': authorization, - 'policy': base64Policy, - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': 'v0.api.upyun.com', - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - 'Date': date, - 'Authorization': authorization, - 'Content-MD5': assetFileMd5, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - '$host/$upyunBucket', - data: formData, + final upyunConfig = UpyunConfigModel( + upyunBucket, + upyunOperator, + upyunPassword, + upyunUrl, + upyunOptions, + upyunPath, + upyunAntiLeechToken, + upyunAntiLeechExpiration, ); - - if (response.statusCode == 200) { - final upyunConfig = UpyunConfigModel( - upyunBucket, - upyunOperator, - upyunPassword, - upyunUrl, - upyunOptions, - upyunPath, - ); - final upyunConfigJson = jsonEncode(upyunConfig); - final upyunConfigFile = await upyunFile; - await upyunConfigFile.writeAsString(upyunConfigJson); - showToast("又拍云配置成功"); - } else { - showToast("又拍云验证失败"); - } + final upyunConfigJson = jsonEncode(upyunConfig); + final upyunConfigFile = await upyunFile; + await upyunConfigFile.writeAsString(upyunConfigJson); + showToast("又拍云配置成功"); } catch (e) { FLog.error( className: 'UpyunConfigPage', diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 01c7190..159792e 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -1,4 +1,4 @@ -import 'dart:io' as io; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -181,7 +181,7 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin value.path); String timeStamp = DateTime.now().millisecondsSinceEpoch.toString(); String randomString = randomStringGenerator(5); - io.File file = io.File('$tempPath/Web$timeStamp$randomString.jpg'); + File file = File('$tempPath/Web$timeStamp$randomString.jpg'); await file.writeAsBytes(response.bodyBytes); Global.imageFile = file.path; @@ -248,17 +247,16 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin maps = {}; if (Global.defaultPShost == 'sm.ms') { //["success", formatedURL, returnUrl, pictureKey] maps = { 'path': path, - 'name': name, + 'name': fullName, 'url': uploadResult[2], //返回地址可以直接访问 'PBhost': Global.defaultPShost, 'pictureKey': uploadResult[3], @@ -390,7 +382,7 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin maps = {}; if (Global.defaultPShost == 'sm.ms') { maps = { 'path': path, - 'name': name, + 'name': Global.imagesList[i], 'url': uploadResult[2], 'PBhost': Global.defaultPShost, 'pictureKey': uploadResult[3], @@ -659,7 +638,7 @@ class HomePageState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin { ), bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, + showSelectedLabels: false, + showUnselectedLabels: false, onTap: (value) { setState(() { _selectedIndex = value; @@ -66,7 +68,7 @@ class TabsPageState extends State { label: '相册', ), BottomNavigationBarItem( - icon: Icon(IconData(0xe6ab, fontFamily: 'iconfont')), + icon: Icon(Icons.storage), label: '仓库', ), BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'), diff --git a/lib/pages/upload_pages/upload_status.dart b/lib/pages/upload_pages/upload_status.dart index 60f8fb5..d87feff 100644 --- a/lib/pages/upload_pages/upload_status.dart +++ b/lib/pages/upload_pages/upload_status.dart @@ -1,6 +1,6 @@ enum UploadStatus { queued, uploading, completed, failed, paused, canceled } -extension UploadStatueExtension on UploadStatus { +extension UploadStatusExtension on UploadStatus { bool get isCompleted { switch (this) { case UploadStatus.queued: diff --git a/lib/pages/upload_pages/upload_utils.dart b/lib/pages/upload_pages/upload_utils.dart index ff709e2..c9395b7 100644 --- a/lib/pages/upload_pages/upload_utils.dart +++ b/lib/pages/upload_pages/upload_utils.dart @@ -4,18 +4,16 @@ import 'dart:convert'; import 'dart:io'; // ignore: depend_on_referenced_packages import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:f_logs/f_logs.dart'; -import 'package:path/path.dart' as my_path; +import 'package:horopic/api/api.dart'; import 'package:ftpconnect/ftpconnect.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:minio/minio.dart'; -import 'package:webdav_client/webdav_client.dart' as webdav; import 'package:horopic/pages/upload_pages/upload_request.dart'; import 'package:horopic/pages/upload_pages/upload_status.dart'; @@ -24,13 +22,7 @@ import 'package:horopic/utils/event_bus_utils.dart'; import 'package:horopic/utils/uploader.dart'; import 'package:horopic/utils/global.dart'; import 'package:horopic/utils/common_functions.dart'; -import 'package:horopic/utils/dio_proxy_adapter.dart'; import 'package:horopic/album/album_sql.dart'; -import 'package:horopic/api/tencent_api.dart'; -import 'package:horopic/api/qiniu_api.dart'; -import 'package:horopic/picture_host_manage/manage_api/alist_manage_api.dart'; -import 'package:horopic/picture_host_manage/manage_api/webdav_manage_api.dart'; -import 'package:horopic/picture_host_configure/configure_page/alist_configure.dart'; class UploadManager { final Map _cache = {}; @@ -69,112 +61,22 @@ class UploadManager { String configData = await readPictureHostConfig(); Map configMap = jsonDecode(configData); String defaultPH = await Global.getPShost(); - Response response; if (defaultPH == 'tencent') { - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String bucket = configMap['bucket']; - String area = configMap['area']; - String tencentpath = configMap['path']; - String customUrl = configMap['customUrl']; - String options = configMap['options']; - if (customUrl != "None") { - if (!customUrl.startsWith(RegExp(r'http(s)?://'))) { - customUrl = 'http://$customUrl'; - } - } - - if (tencentpath != 'None') { - if (tencentpath.startsWith('/')) { - tencentpath = tencentpath.substring(1); - } - if (!tencentpath.endsWith('/')) { - tencentpath = '$tencentpath/'; - } - } - String host = '$bucket.cos.$area.myqcloud.com'; - //云存储的路径 - String urlpath = ''; - if (tencentpath != 'None') { - urlpath = '/$tencentpath$fileName'; - } else { - urlpath = '/$fileName'; - } - int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; - int endTimestamp = startTimestamp + 86400; - String keyTime = '$startTimestamp;$endTimestamp'; - Map uploadPolicy = { - "expiration": "2033-03-03T09:38:12.414Z", - "conditions": [ - {"acl": "default"}, - {"bucket": bucket}, - {"key": urlpath}, - {"q-sign-algorithm": "sha1"}, - {"q-ak": secretId}, - {"q-sign-time": keyTime} - ] - }; - String uploadPolicyStr = jsonEncode(uploadPolicy); - String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'policy': base64Encode(utf8.encode(uploadPolicyStr)), - 'acl': 'default', - 'q-sign-algorithm': 'sha1', - 'q-ak': secretId, - 'q-key-time': keyTime, - 'q-sign-time': keyTime, - 'q-signature': singature, - 'file': await MultipartFile.fromFile(path, filename: my_path.basename(path)), - }); - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(path); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - response = await dio.post( - 'https://$host', - data: formData, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.noContent) { + var tencentUploadResult = await TencentImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (tencentUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = ''; - String displayUrl = ''; - - if (customUrl != 'None') { - if (!customUrl.endsWith('/')) { - returnUrl = '$customUrl$urlpath'; - displayUrl = '$customUrl$urlpath'; - } else { - customUrl = customUrl.substring(0, customUrl.length - 1); - returnUrl = '$customUrl$urlpath'; - displayUrl = '$customUrl$urlpath'; - } - } else { - returnUrl = 'https://$host$urlpath'; - displayUrl = 'https://$host$urlpath'; - } - - if (options == 'None') { - displayUrl = "$displayUrl?imageMogr2/thumbnail/500x500"; - } else { - //网站后缀以?开头 - if (!options.startsWith('?')) { - options = '?$options'; - } - returnUrl = '$returnUrl$options'; - displayUrl = '$displayUrl$options'; - } + String formatedURL = tencentUploadResult[1]; + String returnUrl = tencentUploadResult[2]; + String pictureKey = tencentUploadResult[3]; + String displayUrl = tencentUploadResult[4]; - String formatedURL = ''; if (Global.isCopyLink == true) { formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); } else { @@ -182,8 +84,6 @@ class UploadManager { } await Clipboard.setData(ClipboardData(text: formatedURL)); - Map pictureKeyMap = Map.from(configMap); - String pictureKey = jsonEncode(pictureKeyMap); maps = { 'path': path, 'name': fileName, @@ -198,114 +98,26 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); - } - } else if (defaultPH == 'aliyun') { - String keyId = configMap['keyId']; - String keySecret = configMap['keySecret']; - String bucket = configMap['bucket']; - String area = configMap['area']; - String aliyunpath = configMap['path']; - String customUrl = configMap['customUrl']; - String options = configMap['options']; - //格式化 - if (customUrl != "None") { - if (!customUrl.startsWith('http') && !customUrl.startsWith('https')) { - customUrl = 'http://$customUrl'; - } - } - //格式化 - if (aliyunpath != 'None') { - if (aliyunpath.startsWith('/')) { - aliyunpath = aliyunpath.substring(1); - } - if (!aliyunpath.endsWith('/')) { - aliyunpath = '$aliyunpath/'; - } - } - String host = '$bucket.$area.aliyuncs.com'; - //云存储的路径 - String urlpath = ''; - //阿里云不能以/开头 - if (aliyunpath != 'None') { - urlpath = '$aliyunpath$fileName'; } else { - urlpath = fileName; + throw Exception('上传失败'); } - - Map uploadPolicy = { - "expiration": "2034-12-01T12:00:00.000Z", - "conditions": [ - {"bucket": bucket}, - ["content-length-range", 0, 104857600], - {"key": urlpath} - ] - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'OSSAccessKeyId': keyId, - 'policy': base64Policy, - 'Signature': singature, - 'x-oss-content-type': 'image/${my_path.extension(path).replaceFirst('.', '')}', - 'file': await MultipartFile.fromFile(path, filename: my_path.basename(path)), - }); - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(path); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - response = await dio.post( - 'https://$host', - data: formData, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.noContent) { + } else if (defaultPH == 'aliyun') { + var aliUploadResult = await AliyunImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + if (aliUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = ''; - String displayUrl = ''; - - if (customUrl != 'None') { - if (!customUrl.endsWith('/')) { - returnUrl = '$customUrl/$urlpath'; - displayUrl = '$customUrl/$urlpath'; - } else { - customUrl = customUrl.substring(0, customUrl.length - 1); - returnUrl = '$customUrl/$urlpath'; - displayUrl = '$customUrl/$urlpath'; - } - } else { - returnUrl = 'https://$host/$urlpath'; - displayUrl = 'https://$host/$urlpath'; - } - - if (options == 'None') { - displayUrl = "$displayUrl?x-oss-process=image/resize,m_lfit,h_500,w_500"; - } else { - //网站后缀以?开头 - if (!options.startsWith('?')) { - options = '?$options'; - } - returnUrl = '$returnUrl$options'; - displayUrl = '$displayUrl$options'; - } + String formatedURL = aliUploadResult[1]; + String returnUrl = aliUploadResult[2]; + String pictureKey = aliUploadResult[3]; + String displayUrl = aliUploadResult[4]; - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } await Clipboard.setData(ClipboardData(text: formatedURL)); - String pictureKey = jsonEncode(configMap); + maps = { 'path': path, 'name': fileName, @@ -320,85 +132,27 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); - } - } else if (defaultPH == 'qiniu') { - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String bucket = configMap['bucket']; - String url = configMap['url']; - String area = configMap['area']; - String options = configMap['options']; - String qiniupath = configMap['path']; - - if (!url.startsWith('http') && !url.startsWith('https')) { - url = 'http://$url'; - } - if (url.endsWith('/')) { - url = url.substring(0, url.length - 1); - } - String urlpath = ''; - //不为None才处理 - if (qiniupath != 'None') { - if (qiniupath.startsWith('/')) { - qiniupath = qiniupath.substring(1); - } - if (!qiniupath.endsWith('/')) { - qiniupath = '$qiniupath/'; - } - urlpath = '$qiniupath$fileName'; } else { - urlpath = fileName; + throw Exception('上传失败'); } - String key = fileName; - - String urlSafeBase64EncodePutPolicy = - QiniuImageUploadUtils.geturlSafeBase64EncodePutPolicy(bucket, key, qiniupath); - String uploadToken = QiniuImageUploadUtils.getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); - String host = QiniuImageUploadUtils.areaHostMap[area]!; - FormData formData = FormData.fromMap({ - "key": urlpath, - "fileName": fileName, - "token": uploadToken, - "file": await MultipartFile.fromFile(path, filename: my_path.basename(path)), - }); - BaseOptions baseoptions = setBaseOptions(); - //不需要加Content-Type,host,Content-Length - baseoptions.headers = { - 'Authorization': 'UpToken $uploadToken', - }; - Dio dio = Dio(baseoptions); - response = await dio.post( - host, - data: formData, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - - if (response.statusCode == HttpStatus.ok) { + } else if (defaultPH == 'qiniu') { + var qiniuUploadResult = await QiniuImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (qiniuUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = ''; - String displayUrl = ''; + String formatedURL = qiniuUploadResult[1]; + String returnUrl = qiniuUploadResult[2]; + String pictureKey = qiniuUploadResult[3]; + String displayUrl = qiniuUploadResult[4]; - if (options == 'None') { - returnUrl = '$url/${response.data['key']}'; - displayUrl = '$url/${response.data['key']}?imageView2/2/w/500/h/500'; - } else { - if (!options.startsWith('?')) { - options = '?$options'; - } - returnUrl = '$url/${response.data['key']}$options'; - displayUrl = '$url/${response.data['key']}$options'; - } - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } await Clipboard.setData(ClipboardData(text: formatedURL)); - Map pictureKeyMap = Map.from(configMap); - String pictureKey = jsonEncode(pictureKeyMap); + maps = { 'path': path, 'name': fileName, @@ -413,111 +167,26 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); - } - } else if (defaultPH == 'upyun') { - String bucket = configMap['bucket']; - String upyunOperator = configMap['operator']; - String password = configMap['password']; - String url = configMap['url']; - String options = configMap['options']; - String upyunpath = configMap['path']; - if (options == ' ' || options.trim() == '') { - options = ''; - } - //格式化 - if (url != "None") { - if (!url.startsWith('http') && !url.startsWith('https')) { - url = 'http://$url'; - } - } - //格式化 - if (upyunpath != 'None') { - if (upyunpath.startsWith('/')) { - upyunpath = upyunpath.substring(1); - } - if (!upyunpath.endsWith('/')) { - upyunpath = '$upyunpath/'; - } - } - String host = 'http://v0.api.upyun.com'; - //云存储的路径 - String urlpath = ''; - if (upyunpath != 'None') { - urlpath = '/$upyunpath$fileName'; } else { - urlpath = '/$fileName'; + throw Exception('上传失败'); } - String date = HttpDate.format(DateTime.now()); - File uploadFile = File(path); - String uploadFileMd5 = await uploadFile.readAsBytes().then((value) { - return md5.convert(value).toString(); - }); - Map uploadPolicy = { - 'bucket': bucket, - 'save-key': urlpath, - 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, - 'date': date, - 'content-md5': uploadFileMd5, - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String stringToSign = 'POST&/$bucket&$date&$base64Policy&$uploadFileMd5'; - String passwordMd5 = md5.convert(utf8.encode(password)).toString(); - String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); - String authorization = 'UPYUN $upyunOperator:$signature'; - FormData formData = FormData.fromMap({ - 'authorization': authorization, - 'policy': base64Policy, - 'file': await MultipartFile.fromFile(path, filename: my_path.basename(path)), - }); - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': 'v0.api.upyun.com', - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - 'Date': date, - 'Authorization': authorization, - 'Content-MD5': uploadFileMd5, - }; - Dio dio = Dio(baseoptions); - response = await dio.post( - '$host/$bucket', - data: formData, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.ok) { + } else if (defaultPH == 'upyun') { + var upyunUploadResult = await UpyunImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (upyunUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = ''; - String displayUrl = ''; - if (urlpath.startsWith('/')) { - urlpath = urlpath.substring(1); - } - - if (!url.endsWith('/')) { - returnUrl = '$url/$urlpath'; - displayUrl = '$url/$urlpath'; - } else { - url = url.substring(0, url.length - 1); - returnUrl = '$url/$urlpath'; - displayUrl = '$url/$urlpath'; - } - - returnUrl = '$returnUrl$options'; - displayUrl = '$displayUrl$options'; + String formatedURL = upyunUploadResult[1]; + String returnUrl = upyunUploadResult[2]; + String pictureKey = upyunUploadResult[3]; + String displayUrl = upyunUploadResult[4]; - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } await Clipboard.setData(ClipboardData(text: formatedURL)); - Map pictureKeyMap = Map.from(configMap); - String pictureKey = jsonEncode(pictureKeyMap); maps = { 'path': path, 'name': fileName, @@ -532,56 +201,27 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); - } - } else if (defaultPH == 'lsky.pro') { - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: my_path.basename(path)), - }); - if (configMap["strategy_id"] == "None") { - formdata = FormData.fromMap({}); - } else if (configMap["album_id"] == "None") { - formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: my_path.basename(path)), - "strategy_id": configMap["strategy_id"], - }); } else { - formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: my_path.basename(path)), - "strategy_id": configMap["strategy_id"], - "album_id": configMap["album_id"], - }); + throw Exception('上传失败'); } - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Accept": "application/json", - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); - String uploadUrl = configMap["host"] + "/api/v1/upload"; - response = await dio.post( - uploadUrl, - data: formdata, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.ok && response.data!['status'] == true) { + } else if (defaultPH == 'lsky.pro') { + var lskyproUploadResult = await LskyproImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (lskyproUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = ''; - String displayUrl = ''; - returnUrl = response.data!['data']['links']['url']; - displayUrl = response.data!['data']['links']['thumbnail_url']; - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } + String formatedURL = lskyproUploadResult[1]; + String returnUrl = lskyproUploadResult[2]; + String pictureKey = lskyproUploadResult[3]; + String displayUrl = lskyproUploadResult[4]; + await Clipboard.setData(ClipboardData(text: formatedURL)); - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['deletekey'] = response.data!['data']['key']; - String pictureKey = jsonEncode(pictureKeyMap); + maps = { 'path': path, 'name': fileName, @@ -596,36 +236,24 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); + } else { + throw Exception('上传失败'); } } else if (defaultPH == 'sm.ms') { - FormData formdata = FormData.fromMap({ - "smfile": await MultipartFile.fromFile(path, filename: my_path.basename(path)), - "format": "json", - }); - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); - String uploadUrl = "https://smms.app/api/v2/upload"; - response = await dio.post( - uploadUrl, - data: formdata, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.ok && response.data!['success'] == true) { + var smmsUploadResult = await SmmsImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (smmsUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = response.data!['data']['url']; - String pictureKey = response.data!['data']['hash']; - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } + String formatedURL = smmsUploadResult[1]; + String returnUrl = smmsUploadResult[2]; + String pictureKey = smmsUploadResult[3]; + await Clipboard.setData(ClipboardData(text: formatedURL)); maps = { 'path': path, @@ -641,79 +269,25 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); + } else { + throw Exception('上传失败'); } } else if (defaultPH == 'github') { maxConcurrentTasks = 1; - String base64Image = base64Encode(File(path).readAsBytesSync()); - Map queryBody = { - 'message': 'uploaded by horopic app', - 'content': base64Image, - 'branch': configMap["branch"], //分支 - }; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Accept": "application/vnd.github+json", - }; - - String trimedPath = configMap['storePath'].toString().trim(); - if (trimedPath.startsWith('/')) { - trimedPath = trimedPath.substring(1); - } - if (trimedPath.endsWith('/')) { - trimedPath = trimedPath.substring(0, trimedPath.length - 1); - } - Dio dio = Dio(options); - String uploadUrl = ''; - if (trimedPath == 'None') { - uploadUrl = - "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$fileName"; - } else { - uploadUrl = - "https://api.github.com/repos/${configMap["githubusername"]}/${configMap["repo"]}/contents/$trimedPath/$fileName"; - } - - response = await dio.put( - uploadUrl, - data: jsonEncode(queryBody), + var githubUploadResult = await GithubImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, onSendProgress: createCallback(path, fileName), ); - if (response.statusCode == HttpStatus.ok || response.statusCode == HttpStatus.created) { + if (githubUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = response.data!['content']['html_url']; - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['sha'] = response.data!['content']['sha']; - String pictureKey = jsonEncode(pictureKeyMap); - String downloadUrl = ''; - String formatedURL = ''; - if (configMap['customDomain'] != 'None') { - if (configMap['customDomain'].toString().endsWith('/')) { - String trimedCustomDomain = - configMap['customDomain'].toString().substring(0, configMap['customDomain'].toString().length - 1); - if (trimedPath == 'None') { - downloadUrl = '$trimedCustomDomain$fileName'; - } else { - downloadUrl = '$trimedCustomDomain$trimedPath/$fileName'; - } - } else { - if (trimedPath == 'None') { - downloadUrl = '${configMap['customDomain']}/$fileName'; - } else { - downloadUrl = '${configMap['customDomain']}/$trimedPath/$fileName'; - } - } - } else { - downloadUrl = response.data!['content']['download_url']; - } - if (!downloadUrl.startsWith('http') && !downloadUrl.startsWith('https')) { - downloadUrl = 'http://$downloadUrl'; - } - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(downloadUrl, fileName); - } else { - formatedURL = downloadUrl; - } + String formatedURL = githubUploadResult[1]; + String returnUrl = githubUploadResult[2]; + String pictureKey = githubUploadResult[3]; + String downloadUrl = githubUploadResult[4]; + await Clipboard.setData(ClipboardData(text: formatedURL)); maps = { 'path': path, @@ -729,55 +303,24 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); + } else { + throw Exception('上传失败'); } } else if (defaultPH == 'imgur') { - String base64Image = base64Encode(File(path).readAsBytesSync()); - FormData formdata = FormData.fromMap({ - "image": base64Image, - }); - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": "Client-ID ${configMap["clientId"]}", - }; - Dio dio = Dio(options); - String proxy = configMap["proxy"]; - String proxyClean = ''; - //判断是否有代理 - if (proxy != 'None') { - if (proxy.startsWith('http://') || proxy.startsWith('https://')) { - proxyClean = proxy.split('://')[1]; - } else { - proxyClean = proxy; - } - dio.httpClientAdapter = useProxy(proxyClean); - } - String uploadUrl = "https://api.imgur.com/3/image"; - response = await dio.post( - uploadUrl, - data: formdata, - onSendProgress: createCallback(path, fileName), - cancelToken: canceltoken, - ); - if (response.statusCode == HttpStatus.ok || response.data!['success'] == true) { + var imgurUploadResult = await ImgurImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, + onSendProgress: createCallback(path, fileName), + cancelToken: canceltoken); + + if (imgurUploadResult[0] == 'success') { eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); Map maps = {}; - String returnUrl = response.data!['data']['link']; - Map pictureKeyMap = { - 'clientId': configMap['clientId'], - 'deletehash': response.data!['data']['deletehash'], - }; - String pictureKey = jsonEncode(pictureKeyMap); - - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); - } else { - formatedURL = returnUrl; - } - //相册显示地址用cdn加速,但是复制的时候还是用原图地址 - //https://search.pstatic.net/common?src= - - String cdnUrl = 'https://search.pstatic.net/common?src=$returnUrl'; + String formatedURL = imgurUploadResult[1]; + String returnUrl = imgurUploadResult[2]; + String pictureKey = imgurUploadResult[3]; + String cdnUrl = imgurUploadResult[4]; await Clipboard.setData(ClipboardData(text: formatedURL)); maps = { 'path': path, @@ -793,6 +336,8 @@ class UploadManager { }; await AlbumSQL.insertData(Global.imageDB!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); + } else { + throw Exception('上传失败'); } } else if (defaultPH == 'ftp') { String ftpHost = configMap["ftpHost"]; @@ -1111,190 +656,74 @@ class UploadManager { await AlbumSQL.insertData(Global.imageDBExtend!, pBhostToTableName[Global.defaultPShost]!, maps); setStatus(task, UploadStatus.completed); } else if (defaultPH == 'alist') { - String uploadPath = configMap['uploadPath']; - String token = configMap['token']; - String today = getToday('yyyyMMdd'); - String alistToday = await Global.getTodayAlistUpdate(); - if (alistToday != today && token != '') { - var res = await AlistManageAPI.getToken(configMap['host'], configMap['alistusername'], configMap['password']); - if (res[0] == 'success') { - token = res[1]; - final alistConfig = AlistConfigModel( - configMap['host'], - configMap['alistusername'], - configMap['password'], - token, - uploadPath, - ); - final alistConfigJson = jsonEncode(alistConfig); - final alistConfigFile = await AlistConfigState().localFile; - alistConfigFile.writeAsString(alistConfigJson); - await Global.setTodayAlistUpdate(today); - } else { - throw 'tokenError'; - } - } - - if (uploadPath != 'None') { - if (uploadPath.startsWith('/')) { - uploadPath = uploadPath.substring(1); - } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; - } - } else { - uploadPath = '/'; - } - //云存储的路径 - String filePath = uploadPath + fileName; - - BaseOptions options = setBaseOptions(); - File uploadFile = File(path); - int contentLength = await uploadFile.length().then((value) { - return value; - }); - options.headers = { - "Authorization": token, - "Content-Type": Global.multipartString, - "file-path": Uri.encodeComponent(filePath), - "Content-Length": contentLength, - }; - Dio dio = Dio(options); - String uploadUrl = configMap["host"] + "/api/fs/form"; - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: fileName), - }); - var response = await dio.put( - uploadUrl, - data: formdata, + var alistUploadResult = await AlistImageUploadUtils.uploadApi( + path: path, + name: fileName, + configMap: configMap, onSendProgress: createCallback(path, fileName), ); - if (response.statusCode == HttpStatus.ok && response.data!['message'] == 'success') { - String infoGetUrl = configMap["host"] + "/api/fs/get"; - String refreshUrl = configMap["host"] + "/api/fs/list"; - BaseOptions getOptions = setBaseOptions(); - getOptions.headers = { - "Authorization": configMap["token"], - "Content-Type": "application/json", - }; - Dio dioGet = Dio(getOptions); - Dio dioRefresh = Dio(getOptions); - Map getformData = { - "path": filePath, - }; - Map refreshListFormData = {"password": "", "page": 1, "per_page": 1, "path": uploadPath, "refresh": true}; - var refreshResponse = await dioRefresh.post(refreshUrl, data: refreshListFormData); - if (refreshResponse.statusCode == 200 && refreshResponse.data!['message'] == 'success') { - var responseGet = await dioGet.post(infoGetUrl, data: getformData); - if (responseGet.statusCode == 200 && responseGet.data['message'] == 'success') { - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - Map maps = {}; - String returnUrl = responseGet.data!['data']['raw_url']; - //返回缩略图地址用来在相册显示 - String displayUrl = responseGet.data!['data']['thumb'] == "" || responseGet.data!['data']['thumb'] == null - ? returnUrl - : responseGet.data!['data']['thumb']; - String hostPicUrl = responseGet.data!['data']['sign'] == "" || responseGet.data!['data']['sign'] == null - ? returnUrl - : configMap['host'] + '/d/' + filePath + '?sign=' + responseGet.data!['data']['sign']; + if (alistUploadResult[0] == 'success') { + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + Map maps = {}; + String formatedURL = alistUploadResult[1]; + String returnUrl = alistUploadResult[2]; + String pictureKey = alistUploadResult[3]; + //返回缩略图地址用来在相册显示 + String displayUrl = alistUploadResult[4]; + String hostPicUrl = alistUploadResult[5]; - String formatedURL = ''; - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(hostPicUrl, fileName); - } else { - formatedURL = hostPicUrl; - } - await Clipboard.setData(ClipboardData(text: formatedURL)); + await Clipboard.setData(ClipboardData(text: formatedURL)); - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['sign'] = responseGet.data!['data']['sign']; - pictureKeyMap['uploadPath'] = uploadPath; - pictureKeyMap['filenames'] = fileName; - String pictureKey = jsonEncode(pictureKeyMap); - maps = { - 'path': path, - 'name': fileName, - 'url': returnUrl, - 'PBhost': Global.defaultPShost, - 'pictureKey': pictureKey, - 'hostSpecificArgA': displayUrl, - 'hostSpecificArgB': hostPicUrl, - }; - List letter = 'CDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); - for (int i = 0; i < letter.length; i++) { - maps['hostSpecificArg${letter[i]}'] = 'test'; - } - await AlbumSQL.insertData(Global.imageDBExtend!, pBhostToTableName[Global.defaultPShost]!, maps); - setStatus(task, UploadStatus.completed); - } + maps = { + 'path': path, + 'name': fileName, + 'url': returnUrl, + 'PBhost': Global.defaultPShost, + 'pictureKey': pictureKey, + 'hostSpecificArgA': displayUrl, + 'hostSpecificArgB': hostPicUrl, + }; + List letter = 'CDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); + for (int i = 0; i < letter.length; i++) { + maps['hostSpecificArg${letter[i]}'] = 'test'; } + await AlbumSQL.insertData(Global.imageDBExtend!, pBhostToTableName[Global.defaultPShost]!, maps); + setStatus(task, UploadStatus.completed); + } else { + throw Exception('上传失败'); } } else if (defaultPH == 'webdav') { - String formatedURL = ''; - webdav.Client client = await WebdavManageAPI.getWebdavClient(); - String uploadPath = configMap['uploadPath']; - String? customUrl = configMap['customUrl']; - String? webPath = configMap['webPath']; + var webdavUploadResult = + await WebdavImageUploadUtils.uploadApi(path: path, name: fileName, configMap: configMap); + if (webdavUploadResult[0] == 'success') { + String formatedURL = webdavUploadResult[1]; + String returnUrl = webdavUploadResult[2]; + String pictureKey = webdavUploadResult[3]; + String displayUrl = webdavUploadResult[4]; - customUrl ??= 'None'; - webPath ??= 'None'; + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + Map maps = {}; - if (uploadPath == 'None') { - uploadPath = '/'; - } else { - if (!uploadPath.startsWith('/')) { - uploadPath = '/$uploadPath'; - } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; - } - } - String filePath = uploadPath + fileName; - await client.writeFromFile(path, filePath); - String returnUrl = ''; - String displayUrl = ''; - if (customUrl != 'None') { - customUrl = customUrl.replaceAll(RegExp(r'/$'), ''); - if (webPath != 'None') { - webPath = webPath.replaceAll(RegExp(r'^/*'), '').replaceAll(RegExp(r'/*$'), ''); - returnUrl = '$customUrl/$webPath/$fileName'; - } else { - filePath = filePath.replaceAll(RegExp(r'^/*'), ''); - returnUrl = '$customUrl/$filePath'; - } - displayUrl = returnUrl; - } else { - returnUrl = configMap['host'] + filePath; - displayUrl = returnUrl + generateBasicAuth(configMap['webdavusername'], configMap['password']); - } - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - Map maps = {}; + await Clipboard.setData(ClipboardData(text: formatedURL)); - if (Global.isCopyLink == true) { - formatedURL = linkGenerateDict[Global.defaultLKformat]!(returnUrl, fileName); + maps = { + 'path': path, + 'name': fileName, + 'url': returnUrl, + 'PBhost': Global.defaultPShost, + 'pictureKey': pictureKey, + 'hostSpecificArgA': displayUrl, + }; + List letter = 'BCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); + for (int i = 0; i < letter.length; i++) { + maps['hostSpecificArg${letter[i]}'] = 'test'; + } + await AlbumSQL.insertData(Global.imageDBExtend!, pBhostToTableName[Global.defaultPShost]!, maps); + setStatus(task, UploadStatus.completed); } else { - formatedURL = returnUrl; + throw 'webdavUploadError'; } - await Clipboard.setData(ClipboardData(text: formatedURL)); - - Map pictureKeyMap = Map.from(configMap); - pictureKeyMap['pictureKey'] = filePath; - String pictureKey = jsonEncode(pictureKeyMap); - maps = { - 'path': path, - 'name': fileName, - 'url': returnUrl, - 'PBhost': Global.defaultPShost, - 'pictureKey': pictureKey, - 'hostSpecificArgA': displayUrl, - }; - List letter = 'BCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); - for (int i = 0; i < letter.length; i++) { - maps['hostSpecificArg${letter[i]}'] = 'test'; - } - await AlbumSQL.insertData(Global.imageDBExtend!, pBhostToTableName[Global.defaultPShost]!, maps); - setStatus(task, UploadStatus.completed); } } catch (e) { FLog.error( @@ -1308,11 +737,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_configure/configure_page/alist_configure.dart b/lib/picture_host_configure/configure_page/alist_configure.dart index 54a65fd..ca298f2 100644 --- a/lib/picture_host_configure/configure_page/alist_configure.dart +++ b/lib/picture_host_configure/configure_page/alist_configure.dart @@ -39,8 +39,8 @@ class AlistConfigState extends State { _initConfig() async { try { Map configMap = await AlistManageAPI.getConfigMap(); - _hostController.text = configMap['host']; - _tokenController = configMap['token']; + _hostController.text = configMap['host'] ?? ''; + _tokenController = configMap['token'] ?? ''; if (configMap['alistusername'] != 'None' && configMap['alistusername'] != null) { _usernameController.text = configMap['alistusername']; } else { @@ -125,7 +125,6 @@ class AlistConfigState extends State { ), TextFormField( controller: _passwdController, - obscureText: true, decoration: const InputDecoration( label: Center(child: Text('可选:密码')), hintText: '输入密码', @@ -246,10 +245,7 @@ class AlistConfigState extends State { final alistConfigFile = await localFile; alistConfigFile.writeAsString(alistConfigJson); setState(() {}); - if (context.mounted) { - return showCupertinoAlertDialog( - context: context, barrierDismissible: false, title: '配置成功', content: '配置成功,请返回上一页'); - } + showToast('保存成功'); return; } if (_usernameController.text.isNotEmpty && _passwdController.text.isNotEmpty) { @@ -340,9 +336,8 @@ class AlistConfigState extends State { checkAlistConfig() async { try { - final alistConfigFile = await localFile; - String configData = await alistConfigFile.readAsString(); - if (configData == "Error") { + String configData = await readAlistConfig(); + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -428,7 +423,7 @@ class AlistConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_alist_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_alist_config.txt')); } Future get _localPath async { @@ -437,37 +432,17 @@ class AlistConfigState extends State { } Future readAlistConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'AlistConfigPage', - methodName: 'readAlistConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('alist'); - await Global.setShowedPBhost('PBhostExtend3'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置Alist为默认图床'); - } catch (e) { - FLog.error( - className: 'alistPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('alist'); + await Global.setShowedPBhost('PBhostExtend3'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置Alist为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/aliyun_configure.dart b/lib/picture_host_configure/configure_page/aliyun_configure.dart index cf0758b..ba12481 100644 --- a/lib/picture_host_configure/configure_page/aliyun_configure.dart +++ b/lib/picture_host_configure/configure_page/aliyun_configure.dart @@ -45,21 +45,21 @@ class AliyunConfigState extends State { _initConfig() async { try { Map configMap = await AliyunManageAPI.getConfigMap(); - _keyIdController.text = configMap['keyId']; - _keySecretController.text = configMap['keySecret']; - _bucketController.text = configMap['bucket']; - _areaController.text = configMap['area']; - if (configMap['path'] != 'None') { + _keyIdController.text = configMap['keyId'] ?? ''; + _keySecretController.text = configMap['keySecret'] ?? ''; + _bucketController.text = configMap['bucket'] ?? ''; + _areaController.text = configMap['area'] ?? ''; + if (configMap['path'] != 'None' && configMap['path'] != null) { _pathController.text = configMap['path']; } else { _pathController.clear(); } - if (configMap['customUrl'] != 'None') { + if (configMap['customUrl'] != 'None' && configMap['customUrl'] != null) { _customUrlController.text = configMap['customUrl']; } else { _customUrlController.clear(); } - if (configMap['options'] != 'None') { + if (configMap['options'] != 'None' && configMap['options'] != null) { _optionsController.text = configMap['options']; } else { _optionsController.clear(); @@ -268,7 +268,7 @@ class AliyunConfigState extends State { String customUrl = _customUrlController.text; String options = _optionsController.text; //格式化路径为以/结尾,不以/开头 - if (path.isEmpty || path.replaceAll(' ', '').isEmpty) { + if (path.isEmpty || path.replaceAll(' ', '').isEmpty || path == '/') { path = 'None'; } else { if (!path.endsWith('/')) { @@ -297,76 +297,11 @@ class AliyunConfigState extends State { options = 'None'; } - //save asset image to app dir - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - String host = '$bucket.$area.aliyuncs.com'; - String urlpath = ''; - if (path != 'None') { - urlpath = '$path$key'; - } else { - urlpath = key; - } - - Map uploadPolicy = { - "expiration": "2034-12-01T12:00:00.000Z", - "conditions": [ - {"bucket": bucket}, - ["content-length-range", 0, 104857600], - {"key": urlpath} - ] - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'OSSAccessKeyId': keyId, - 'policy': base64Policy, - 'Signature': singature, - //阿里默认的content-type是application/octet-stream,这里改成image/xxx - 'x-oss-content-type': 'image/${my_path.extension(assetFilePath).replaceFirst('.', '')}', - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - 'https://$host', - data: formData, - ); - //阿里默认返回204 - if (response.statusCode == 204) { - final aliyunConfig = AliyunConfigModel(keyId, keySecret, bucket, area, path, customUrl, options); - final aliyunConfigJson = jsonEncode(aliyunConfig); - final aliyunConfigFile = await localFile; - await aliyunConfigFile.writeAsString(aliyunConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: '验证失败'); - } - } + final aliyunConfig = AliyunConfigModel(keyId, keySecret, bucket, area, path, customUrl, options); + final aliyunConfigJson = jsonEncode(aliyunConfig); + final aliyunConfigFile = await localFile; + await aliyunConfigFile.writeAsString(aliyunConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'AliyunConfigPage', @@ -381,10 +316,9 @@ class AliyunConfigState extends State { checkAliyunConfig() async { try { - final aliyunConfigFile = await localFile; - String configData = await aliyunConfigFile.readAsString(); + String configData = await readAliyunConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -480,7 +414,7 @@ class AliyunConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_aliyun_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_aliyun_config.txt')); } Future get _localPath async { @@ -489,37 +423,16 @@ class AliyunConfigState extends State { } Future readAliyunConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'AliyunConfigPage', - methodName: 'readAliyunConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('aliyun'); - await Global.setShowedPBhost('aliyun'); - showToast('已设置阿里云为默认图床'); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - } catch (e) { - FLog.error( - className: 'AliyunConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('aliyun'); + await Global.setShowedPBhost('aliyun'); + showToast('已设置阿里云为默认图床'); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); } } diff --git a/lib/picture_host_configure/configure_page/aws_configure.dart b/lib/picture_host_configure/configure_page/aws_configure.dart index 7d167a4..c18740d 100644 --- a/lib/picture_host_configure/configure_page/aws_configure.dart +++ b/lib/picture_host_configure/configure_page/aws_configure.dart @@ -41,24 +41,24 @@ class AwsConfigState extends State { _initConfig() async { try { Map configMap = await AwsManageAPI.getConfigMap(); - _accessKeyIDController.text = configMap['accessKeyId']; - _secretAccessKeyController.text = configMap['secretAccessKey']; - _bucketController.text = configMap['bucket']; - _endpointController.text = configMap['endpoint']; + _accessKeyIDController.text = configMap['accessKeyId'] ?? ''; + _secretAccessKeyController.text = configMap['secretAccessKey'] ?? ''; + _bucketController.text = configMap['bucket'] ?? ''; + _endpointController.text = configMap['endpoint'] ?? ''; - if (configMap['region'] != 'None') { + if (configMap['region'] != 'None' && configMap['region'] != null) { _regionController.text = configMap['region']; } else { _regionController.clear(); } - if (configMap['uploadPath'] != 'None') { + if (configMap['uploadPath'] != 'None' && configMap['uploadPath'] != null) { _uploadPathController.text = configMap['uploadPath']; } else { _uploadPathController.clear(); } - if (configMap['customUrl'] != 'None') { + if (configMap['customUrl'] != 'None' && configMap['customUrl'] != null) { _customUrlController.text = configMap['customUrl']; } else { _customUrlController.clear(); @@ -265,7 +265,7 @@ class AwsConfigState extends State { String uploadPath = _uploadPathController.text; String customUrl = _customUrlController.text; //格式化路径为以/结尾,不以/开头 - if (uploadPath.isEmpty || uploadPath.trim().isEmpty) { + if (uploadPath.isEmpty || uploadPath.trim().isEmpty || uploadPath == '/') { uploadPath = 'None'; } else { if (!uploadPath.endsWith('/')) { @@ -301,9 +301,7 @@ class AwsConfigState extends State { final awsConfigJson = jsonEncode(awsConfig); final awsConfigFile = await localFile; await awsConfigFile.writeAsString(awsConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } + showToast('保存成功'); } catch (e) { FLog.error( className: 'AwsConfigPage', @@ -318,10 +316,9 @@ class AwsConfigState extends State { checkAwsConfig() async { try { - final awsConfigFile = await localFile; - String configData = await awsConfigFile.readAsString(); + String configData = await readAwsConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -373,7 +370,7 @@ class AwsConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_aws_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_aws_config.txt')); } Future get _localPath async { @@ -382,37 +379,17 @@ class AwsConfigState extends State { } Future readAwsConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'AwsConfigPage', - methodName: 'readAwsConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('aws'); - await Global.setShowedPBhost('PBhostExtend2'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置S3兼容平台为默认图床'); - } catch (e) { - FLog.error( - className: 'AwsConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('aws'); + await Global.setShowedPBhost('PBhostExtend2'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置S3兼容平台为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/ftp_configure.dart b/lib/picture_host_configure/configure_page/ftp_configure.dart index aa141d0..87b3afa 100644 --- a/lib/picture_host_configure/configure_page/ftp_configure.dart +++ b/lib/picture_host_configure/configure_page/ftp_configure.dart @@ -59,27 +59,27 @@ class FTPConfigState extends State { resetFtpConfigMap(); try { Map configMap = await FTPManageAPI.getConfigMap(); - _ftpHostController.text = configMap['ftpHost']; - _ftpPortController.text = configMap['ftpPort']; - _ftpConfigMap['ftpType'] = configMap['ftpType']; - _ftpConfigMap['isAnonymous'] = configMap['isAnonymous'].toString(); + _ftpHostController.text = configMap['ftpHost'] ?? ''; + _ftpPortController.text = configMap['ftpPort'] ?? ''; + _ftpConfigMap['ftpType'] = configMap['ftpType'] ?? ''; + _ftpConfigMap['isAnonymous'] = configMap['isAnonymous']?.toString() ?? ''; - if (configMap['ftpUser'] != 'None') { + if (configMap['ftpUser'] != 'None' && configMap['ftpUser'] != null) { _ftpUserController.text = configMap['ftpUser']; } else { _ftpUserController.clear(); } - if (configMap['ftpPassword'] != 'None') { + if (configMap['ftpPassword'] != 'None' && configMap['ftpPassword'] != null) { _ftpPasswordController.text = configMap['ftpPassword']; } else { _ftpPasswordController.clear(); } - if (configMap['uploadPath'] != 'None') { + if (configMap['uploadPath'] != 'None' && configMap['uploadPath'] != null) { _ftpUploadPathController.text = configMap['uploadPath']; } else { _ftpUploadPathController.clear(); } - if (configMap['ftpHomeDir'] != 'None') { + if (configMap['ftpHomeDir'] != 'None' && configMap['ftpHomeDir'] != null) { _ftpHomeDirController.text = configMap['ftpHomeDir']; } else { _ftpHomeDirController.clear(); @@ -374,7 +374,9 @@ class FTPConfigState extends State { ftpPassword = _ftpPasswordController.text; } String ftpUploadPath = ''; - if (_ftpUploadPathController.text.isEmpty || _ftpUploadPathController.text == '') { + if (_ftpUploadPathController.text.isEmpty || + _ftpUploadPathController.text == '' || + _ftpUploadPathController.text == '/') { ftpUploadPath = 'None'; } else { ftpUploadPath = _ftpUploadPathController.text; @@ -398,7 +400,7 @@ class FTPConfigState extends State { ftpCustomUrl = _ftpCustomUrlController.text; } String ftpWebPath = ''; - if (_ftpWebPathController.text.isEmpty || _ftpWebPathController.text == '') { + if (_ftpWebPathController.text.isEmpty || _ftpWebPathController.text == '' || _ftpWebPathController.text == '/') { ftpWebPath = 'None'; } else { ftpWebPath = _ftpWebPathController.text; @@ -410,57 +412,12 @@ class FTPConfigState extends State { final String ftpType = _ftpConfigMap['ftpType']; try { - try { - if (ftpType == 'FTP') { - FTPConnect ftpConnect; - if (isAnonymous == 'true') { - ftpConnect = FTPConnect(ftpHost, port: int.parse(ftpPort), securityType: SecurityType.FTP); - } else { - ftpConnect = FTPConnect(ftpHost, - port: int.parse(ftpPort), user: ftpUser, pass: ftpPassword, securityType: SecurityType.FTP); - } - bool connectResult = await ftpConnect.connect(); - await ftpConnect.disconnect(); - if (connectResult == true) { - final ftpConfig = FTPConfigModel(ftpHost, ftpPort, ftpUser, ftpPassword, ftpType, isAnonymous, - ftpUploadPath, ftpHomeDir, ftpCustomUrl, ftpWebPath); - final ftpConfigJson = jsonEncode(ftpConfig); - final ftpConfigFile = await localFile; - await ftpConfigFile.writeAsString(ftpConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } - } else { - final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort.toString())); - final client = SSHClient( - socket, - username: ftpUser, - onPasswordRequest: () { - return ftpPassword; - }, - ); - client.close(); - - final ftpConfig = FTPConfigModel(ftpHost, ftpPort, ftpUser, ftpPassword, ftpType, isAnonymous, ftpUploadPath, - ftpHomeDir, ftpCustomUrl, ftpWebPath); - final ftpConfigJson = jsonEncode(ftpConfig); - final ftpConfigFile = await localFile; - await ftpConfigFile.writeAsString(ftpConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } - } catch (e) { - FLog.error( - className: 'FTPConfigPage', - methodName: '_saveFTPConfig_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: e.toString()); - } - } + final ftpConfig = FTPConfigModel(ftpHost, ftpPort, ftpUser, ftpPassword, ftpType, isAnonymous, ftpUploadPath, + ftpHomeDir, ftpCustomUrl, ftpWebPath); + final ftpConfigJson = jsonEncode(ftpConfig); + final ftpConfigFile = await localFile; + await ftpConfigFile.writeAsString(ftpConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'FTPConfigPage', @@ -475,10 +432,9 @@ class FTPConfigState extends State { checkFTPConfig() async { try { - final ftpConfigFile = await localFile; - String configData = await ftpConfigFile.readAsString(); + String configData = await readFTPConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -553,7 +509,7 @@ class FTPConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_ftp_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_ftp_config.txt')); } Future get _localPath async { @@ -562,37 +518,17 @@ class FTPConfigState extends State { } Future readFTPConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'FTPConfigPage', - methodName: 'readFTPConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('ftp'); - await Global.setShowedPBhost('PBhostExtend1'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置FTP为默认图床'); - } catch (e) { - FLog.error( - className: 'FTPConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('ftp'); + await Global.setShowedPBhost('PBhostExtend1'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置FTP为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/github_configure.dart b/lib/picture_host_configure/configure_page/github_configure.dart index 4fc432b..ec42ac1 100644 --- a/lib/picture_host_configure/configure_page/github_configure.dart +++ b/lib/picture_host_configure/configure_page/github_configure.dart @@ -40,16 +40,16 @@ class GithubConfigState extends State { _initConfig() async { try { Map configMap = await GithubManageAPI.getConfigMap(); - _githubusernameController.text = configMap['githubusername']; - _repoController.text = configMap['repo']; - _tokenController.text = configMap['token']; - if (configMap['storePath'] != 'None') { + _githubusernameController.text = configMap['githubusername'] ?? ''; + _repoController.text = configMap['repo'] ?? ''; + _tokenController.text = configMap['token'] ?? ''; + if (configMap['storePath'] != 'None' && configMap['storePath'] != null) { _storePathController.text = configMap['storePath']; } else { _storePathController.clear(); } - _branchController.text = configMap['branch']; - if (configMap['customDomain'] != 'None') { + _branchController.text = configMap['branch'] ?? ''; + if (configMap['customDomain'] != 'None' && configMap['customDomain'] != null) { _customDomainController.text = configMap['customDomain']; } else { _customDomainController.clear(); @@ -228,86 +228,56 @@ class GithubConfigState extends State { } Future _saveGithubConfig() async { - String token = 'Bearer '; - String githubUserApi = 'https://api.github.com/user'; - final String githubusername = _githubusernameController.text; - final String repo = _repoController.text; - String storePath = ''; - if (_storePathController.text.isEmpty || _storePathController.text.trim().isEmpty) { - storePath = 'None'; - } else { - storePath = _storePathController.text; - if (!storePath.endsWith('/')) { - storePath = '$storePath/'; + try { + String token = 'Bearer '; + final String githubusername = _githubusernameController.text; + final String repo = _repoController.text; + String storePath = ''; + if (_storePathController.text.isEmpty || + _storePathController.text.trim().isEmpty || + _storePathController.text == '/') { + storePath = 'None'; + } else { + storePath = _storePathController.text; + if (!storePath.endsWith('/')) { + storePath = '$storePath/'; + } } - } - String branch = ''; - if (_branchController.text.isEmpty || _branchController.text.trim().isEmpty) { - branch = 'main'; - } else { - branch = _branchController.text; - } - String customDomain = ''; - if (_customDomainController.text.isEmpty || _customDomainController.text.trim().isEmpty) { - customDomain = 'None'; - } else { - customDomain = _customDomainController.text; - if (!customDomain.startsWith('http') && !customDomain.startsWith('https')) { - customDomain = 'http://$customDomain'; - } - if (customDomain.endsWith('/')) { - customDomain = customDomain.substring(0, customDomain.length - 1); + String branch = ''; + if (_branchController.text.isEmpty || _branchController.text.trim().isEmpty) { + branch = 'main'; + } else { + branch = _branchController.text; } - } - - if (_tokenController.text.startsWith('Bearer ')) { - token = _tokenController.text; - } else { - token = token + _tokenController.text; - } - - try { - BaseOptions options = setBaseOptions(); - options.headers = { - "Accept": 'application/vnd.github+json', - "Authorization": token, - }; - //需要加一个空的formdata,不然会报错 - Map queryData = {}; - Dio dio = Dio(options); - - try { - var validateResponse = await dio.get(githubUserApi, queryParameters: queryData); - if (validateResponse.statusCode == 200 && validateResponse.data.toString().contains("email")) { - //验证成功 - - final githubConfig = GithubConfigModel(githubusername, repo, token, storePath, branch, customDomain); - final githubConfigJson = jsonEncode(githubConfig); - final githubConfigFile = await localFile; - await githubConfigFile.writeAsString(githubConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: 'token错误'); - } + String customDomain = ''; + if (_customDomainController.text.isEmpty || _customDomainController.text.trim().isEmpty) { + customDomain = 'None'; + } else { + customDomain = _customDomainController.text; + if (!customDomain.startsWith('http') && !customDomain.startsWith('https')) { + customDomain = 'http://$customDomain'; } - } catch (e) { - FLog.error( - className: 'GithubConfigPage', - methodName: '_saveGithubConfig_1', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: e.toString()); + if (customDomain.endsWith('/')) { + customDomain = customDomain.substring(0, customDomain.length - 1); } } + + if (_tokenController.text.startsWith('Bearer ')) { + token = _tokenController.text; + } else { + token = token + _tokenController.text; + } + + final githubConfig = GithubConfigModel(githubusername, repo, token, storePath, branch, customDomain); + final githubConfigJson = jsonEncode(githubConfig); + final githubConfigFile = await localFile; + await githubConfigFile.writeAsString(githubConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'GithubConfigPage', - methodName: '_saveGithubConfig_2', + methodName: '_saveGithubConfig', text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); if (context.mounted) { @@ -318,10 +288,9 @@ class GithubConfigState extends State { checkGithubConfig() async { try { - final githubConfigFile = await localFile; - String configData = await githubConfigFile.readAsString(); + String configData = await readGithubConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -368,7 +337,7 @@ class GithubConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_github_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_github_config.txt')); } Future get _localPath async { @@ -377,37 +346,17 @@ class GithubConfigState extends State { } Future readGithubConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'GithubConfigPage', - methodName: 'readGithubConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('github'); - await Global.setShowedPBhost('github'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置Github为默认图床'); - } catch (e) { - FLog.error( - className: 'GithubConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('github'); + await Global.setShowedPBhost('github'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置Github为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/imgur_configure.dart b/lib/picture_host_configure/configure_page/imgur_configure.dart index 2798e16..4df0697 100644 --- a/lib/picture_host_configure/configure_page/imgur_configure.dart +++ b/lib/picture_host_configure/configure_page/imgur_configure.dart @@ -36,8 +36,8 @@ class ImgurConfigState extends State { _initConfig() async { try { Map configMap = await ImgurManageAPI.getConfigMap(); - _clientIdController.text = configMap['clientId']; - if (configMap['proxy'] != 'None') { + _clientIdController.text = configMap['clientId'] ?? ''; + if (configMap['proxy'] != 'None' && configMap['proxy'] != null) { _proxyController.text = configMap['proxy']; } else { _proxyController.clear(); @@ -164,63 +164,30 @@ class ImgurConfigState extends State { } Future _saveImgurConfig() async { - String clientId = ''; - if (_clientIdController.text.startsWith('Client-ID ')) { - clientId = _clientIdController.text.substring(10); - } else { - clientId = _clientIdController.text; - } - String proxy = ''; - if (_proxyController.text == '' || _proxyController.text.isEmpty) { - proxy = 'None'; - } else { - proxy = _proxyController.text; - } - try { - String baiduPicUrl = - "https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white-d0c9fe2af5.png"; - String validateURL = "https://api.imgur.com/3/image"; - - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": "Client-ID $clientId", - }; - //需要加一个空的formdata,不然会报错 - FormData formData = FormData.fromMap({ - "image": baiduPicUrl, - }); - Dio dio = Dio(options); - - String proxyClean = ''; - if (proxy != 'None') { - if (proxy.startsWith('http://') || proxy.startsWith('https://')) { - proxyClean = proxy.split('://')[1]; - } else { - proxyClean = proxy; - } - dio.httpClientAdapter = useProxy(proxyClean); + String clientId = ''; + if (_clientIdController.text.startsWith('Client-ID ')) { + clientId = _clientIdController.text.substring(10); + } else { + clientId = _clientIdController.text; } - - var validateResponse = await dio.post(validateURL, data: formData); - if (validateResponse.statusCode == 200 && validateResponse.data['success'] == true) { - final imgurConfig = ImgurConfigModel(clientId, proxy); - final imgurConfigJson = jsonEncode(imgurConfig); - final imgurConfigFile = await localFile; - await imgurConfigFile.writeAsString(imgurConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - return; + String proxy = ''; + if (_proxyController.text == '' || _proxyController.text.isEmpty) { + proxy = 'None'; } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: 'clientId错误'); - } + proxy = _proxyController.text; } + + final imgurConfig = ImgurConfigModel(clientId, proxy); + final imgurConfigJson = jsonEncode(imgurConfig); + final imgurConfigFile = await localFile; + await imgurConfigFile.writeAsString(imgurConfigJson); + showToast('保存成功'); + return; } catch (e) { FLog.error( className: 'ImgurConfigPage', - methodName: '_saveImgurConfig_2', + methodName: '_saveImgurConfig', text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); if (context.mounted) { @@ -231,9 +198,8 @@ class ImgurConfigState extends State { checkImgurConfig() async { try { - final imgurConfigFile = await localFile; - String configData = await imgurConfigFile.readAsString(); - if (configData == "Error") { + String configData = await readHostConfig(); + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -291,7 +257,7 @@ class ImgurConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_imgur_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_imgur_config.txt')); } Future get _localPath async { @@ -300,37 +266,17 @@ class ImgurConfigState extends State { } Future readHostConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'ImgurConfigPage', - methodName: 'readHostConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('imgur'); - await Global.setShowedPBhost('imgur'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置Imgur为默认图床'); - } catch (e) { - FLog.error( - className: 'ImgurConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('imgur'); + await Global.setShowedPBhost('imgur'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置Imgur为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/lskypro_configure.dart b/lib/picture_host_configure/configure_page/lskypro_configure.dart index 8062d95..2d06208 100644 --- a/lib/picture_host_configure/configure_page/lskypro_configure.dart +++ b/lib/picture_host_configure/configure_page/lskypro_configure.dart @@ -46,14 +46,14 @@ class HostConfigState extends State { _initConfig() async { try { Map configMap = await LskyproManageAPI.getConfigMap(); - _hostController.text = configMap['host']; - _strategyIdController.text = configMap['strategy_id']; + _hostController.text = configMap['host'] ?? ''; + _strategyIdController.text = configMap['strategy_id'] ?? ''; if (configMap['album_id'] != 'None' && configMap['album_id'] != null) { _albumIdController.text = configMap['album_id']; } else { _albumIdController.clear(); } - _tokenController = configMap['token']; + _tokenController = configMap['token'] ?? ''; setState(() {}); } catch (e) { FLog.error( @@ -121,7 +121,6 @@ class HostConfigState extends State { ), TextFormField( controller: _passwdController, - obscureText: true, decoration: const InputDecoration( label: Center(child: Text('密码')), hintText: '输入密码', @@ -592,9 +591,8 @@ class HostConfigState extends State { checkHostConfig() async { try { - final hostConfigFile = await localFile; - String configData = await hostConfigFile.readAsString(); - if (configData == "Error") { + String configData = await readHostConfig(); + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -648,7 +646,7 @@ class HostConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_host_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_host_config.txt')); } Future get _localPath async { @@ -657,37 +655,17 @@ class HostConfigState extends State { } Future readHostConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'HostConfigPage', - methodName: 'readHostConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('lsky.pro'); - await Global.setShowedPBhost('lskypro'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置兰空图床为默认图床'); - } catch (e) { - FLog.error( - className: 'LskyproPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('lsky.pro'); + await Global.setShowedPBhost('lskypro'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置兰空图床为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/qiniu_configure.dart b/lib/picture_host_configure/configure_page/qiniu_configure.dart index fad5c7c..1f48a3f 100644 --- a/lib/picture_host_configure/configure_page/qiniu_configure.dart +++ b/lib/picture_host_configure/configure_page/qiniu_configure.dart @@ -43,17 +43,17 @@ class QiniuConfigState extends State { _initConfig() async { try { Map configMap = await QiniuManageAPI.getConfigMap(); - _accessKeyController.text = configMap['accessKey']; - _secretKeyController.text = configMap['secretKey']; - _bucketController.text = configMap['bucket']; - _urlController.text = configMap['url']; - _areaController.text = configMap['area']; - if (configMap['options'] != 'None') { + _accessKeyController.text = configMap['accessKey'] ?? ''; + _secretKeyController.text = configMap['secretKey'] ?? ''; + _bucketController.text = configMap['bucket'] ?? ''; + _urlController.text = configMap['url'] ?? ''; + _areaController.text = configMap['area'] ?? ''; + if (configMap['options'] != 'None' && configMap['options'] != null) { _optionsController.text = configMap['options']; } else { _optionsController.clear(); } - if (configMap['path'] != 'None') { + if (configMap['path'] != 'None' && configMap['path'] != null) { _pathController.text = configMap['path']; } else { _pathController.clear(); @@ -280,7 +280,9 @@ class QiniuConfigState extends State { } String path = ''; - if (_pathController.text.isNotEmpty && _pathController.text.replaceAll(' ', '').isNotEmpty) { + if (_pathController.text.isNotEmpty && + _pathController.text.replaceAll(' ', '').isNotEmpty && + _pathController.text != '/') { path = _pathController.text; if (path.startsWith('/')) { path = path.substring(1); @@ -292,42 +294,11 @@ class QiniuConfigState extends State { path = 'None'; } - //save asset image to app dir - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - - String urlSafeBase64EncodePutPolicy = QiniuImageUploadUtils.geturlSafeBase64EncodePutPolicy(bucket, key, path); - String uploadToken = QiniuImageUploadUtils.getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); - Storage storage = Storage( - config: Config( - retryLimit: 5, - )); - PutResponse putresult = await storage.putFile(File(assetFilePath), uploadToken); - - if (putresult.key == key || putresult.key == '$path$key') { - final qiniuConfig = QiniuConfigModel(accessKey, secretKey, bucket, url, area, options, path); - final qiniuConfigJson = jsonEncode(qiniuConfig); - final qiniuConfigFile = await localFile; - await qiniuConfigFile.writeAsString(qiniuConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: '验证失败'); - } - } + final qiniuConfig = QiniuConfigModel(accessKey, secretKey, bucket, url, area, options, path); + final qiniuConfigJson = jsonEncode(qiniuConfig); + final qiniuConfigFile = await localFile; + await qiniuConfigFile.writeAsString(qiniuConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'QiniuConfigPage', @@ -342,10 +313,9 @@ class QiniuConfigState extends State { checkQiniuConfig() async { try { - final qiniuConfigFile = await localFile; - String configData = await qiniuConfigFile.readAsString(); + String configData = await readQiniuConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -416,7 +386,7 @@ class QiniuConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_qiniu_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_qiniu_config.txt')); } Future get _localPath async { @@ -425,37 +395,17 @@ class QiniuConfigState extends State { } Future readQiniuConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'QiniuConfigPage', - methodName: 'readQiniuConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('qiniu'); - await Global.setShowedPBhost('qiniu'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置七牛云为默认图床'); - } catch (e) { - FLog.error( - className: 'QiniuConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('qiniu'); + await Global.setShowedPBhost('qiniu'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置七牛云为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/smms_configure.dart b/lib/picture_host_configure/configure_page/smms_configure.dart index 6d96887..6736ea1 100644 --- a/lib/picture_host_configure/configure_page/smms_configure.dart +++ b/lib/picture_host_configure/configure_page/smms_configure.dart @@ -34,7 +34,7 @@ class SmmsConfigState extends State { _initConfig() async { try { Map configMap = await SmmsManageAPI.getConfigMap(); - _tokenController.text = configMap['token']; + _tokenController.text = configMap['token'] ?? ''; } catch (e) { FLog.error( className: 'SmmsConfigState', @@ -150,34 +150,15 @@ class SmmsConfigState extends State { try { final token = _tokenController.text; - String validateURL = "https://smms.app/api/v2/profile"; - BaseOptions options = setBaseOptions(); - options.headers = { - "Content-Type": 'multipart/form-data', - "Authorization": token, - }; - //需要加一个空的formdata,不然会报错 - FormData formData = FormData.fromMap({}); - Dio dio = Dio(options); - - var validateResponse = await dio.post(validateURL, data: formData); - if (validateResponse.statusCode == 200 && validateResponse.data['success'] == true) { - final smmsConfig = SmmsConfigModel(token); - final smmsConfigJson = jsonEncode(smmsConfig); - final smmsConfigFile = await localFile; - await smmsConfigFile.writeAsString(smmsConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: '配置失败'); - } - } + final smmsConfig = SmmsConfigModel(token); + final smmsConfigJson = jsonEncode(smmsConfig); + final smmsConfigFile = await localFile; + await smmsConfigFile.writeAsString(smmsConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'SmmsConfigState', - methodName: '_saveSmmsConfig_2', + methodName: '_saveSmmsConfig', text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); if (context.mounted) { @@ -188,9 +169,8 @@ class SmmsConfigState extends State { checkSmmsConfig() async { try { - final smmsConfigFile = await localFile; - String configData = await smmsConfigFile.readAsString(); - if (configData == "Error") { + String configData = await readHostConfig(); + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -235,7 +215,7 @@ class SmmsConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_smms_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_smms_config.txt')); } Future get _localPath async { @@ -244,18 +224,9 @@ class SmmsConfigState extends State { } Future readHostConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'SmmsConfigState', - methodName: 'readHostConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { diff --git a/lib/picture_host_configure/configure_page/tencent_configure.dart b/lib/picture_host_configure/configure_page/tencent_configure.dart index e376c69..72eda8a 100644 --- a/lib/picture_host_configure/configure_page/tencent_configure.dart +++ b/lib/picture_host_configure/configure_page/tencent_configure.dart @@ -44,22 +44,22 @@ class TencentConfigState extends State { _initConfig() async { try { Map configMap = await TencentManageAPI.getConfigMap(); - _secretIdController.text = configMap['secretId']; - _secretKeyController.text = configMap['secretKey']; - _bucketController.text = configMap['bucket']; - _appIdController.text = configMap['appId']; - _areaController.text = configMap['area']; - if (configMap['path'] != 'None') { + _secretIdController.text = configMap['secretId'] ?? ''; + _secretKeyController.text = configMap['secretKey'] ?? ''; + _bucketController.text = configMap['bucket'] ?? ''; + _appIdController.text = configMap['appId'] ?? ''; + _areaController.text = configMap['area'] ?? ''; + if (configMap['path'] != 'None' && configMap['path'] != null) { _pathController.text = configMap['path']; } else { _pathController.clear(); } - if (configMap['customUrl'] != 'None') { + if (configMap['customUrl'] != 'None' && configMap['customUrl'] != null) { _customUrlController.text = configMap['customUrl']; } else { _customUrlController.clear(); } - if (configMap['options'] != 'None') { + if (configMap['options'] != 'None' && configMap['options'] != null) { _optionsController.text = configMap['options']; } else { _optionsController.clear(); @@ -282,7 +282,7 @@ class TencentConfigState extends State { String customUrl = _customUrlController.text; String options = _optionsController.text; //格式化路径为以/结尾,不以/开头 - if (path.isEmpty || path.replaceAll(' ', '').isEmpty) { + if (path.isEmpty || path.replaceAll(' ', '').isEmpty || path == '/') { path = 'None'; } else { if (!path.endsWith('/')) { @@ -312,84 +312,11 @@ class TencentConfigState extends State { options = 'None'; } - //save asset image to app dir - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - String host = '$bucket.cos.$area.myqcloud.com'; - String urlpath = ''; - if (path != 'None') { - urlpath = '$path$key'; - } else { - urlpath = key; - } - int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; - int endTimestamp = startTimestamp + 86400; - String keyTime = '$startTimestamp;$endTimestamp'; - Map uploadPolicy = { - "expiration": "2033-03-03T09:38:12.414Z", - "conditions": [ - {"acl": "default"}, - {"bucket": bucket}, - {"key": urlpath}, - {"q-sign-algorithm": "sha1"}, - {"q-ak": secretId}, - {"q-sign-time": keyTime} - ] - }; - String uploadPolicyStr = jsonEncode(uploadPolicy); - String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); - //policy中的字段,除了bucket,其它的都要在formdata中添加 - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'policy': base64Encode(utf8.encode(uploadPolicyStr)), - 'acl': 'default', - 'q-sign-algorithm': 'sha1', - 'q-ak': secretId, - 'q-key-time': keyTime, - 'q-sign-time': keyTime, - 'q-signature': singature, - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - 'http://$host', - data: formData, - ); - //腾讯默认返回204 - if (response.statusCode == 204) { - final tencentConfig = TencentConfigModel(secretId, secretKey, bucket, appId, area, path, customUrl, options); - final tencentConfigJson = jsonEncode(tencentConfig); - final tencentConfigFile = await localFile; - await tencentConfigFile.writeAsString(tencentConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: '数据库错误'); - } - } + final tencentConfig = TencentConfigModel(secretId, secretKey, bucket, appId, area, path, customUrl, options); + final tencentConfigJson = jsonEncode(tencentConfig); + final tencentConfigFile = await localFile; + await tencentConfigFile.writeAsString(tencentConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'TencentConfigPage', @@ -404,10 +331,9 @@ class TencentConfigState extends State { checkTencentConfig() async { try { - final tencentConfigFile = await localFile; - String configData = await tencentConfigFile.readAsString(); + String configData = await readTencentConfig(); - if (configData == "Error") { + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -485,11 +411,24 @@ class TencentConfigState extends State { if (response.statusCode == 204) { if (context.mounted) { - return showCupertinoAlertDialog( - context: context, - title: '通知', - content: - '检测通过,您的配置信息为:\nsecretId:\n${configMap['secretId']}\nsecretKey:\n${configMap['secretKey']}\nbucket:\n${configMap['bucket']}\nappId:\n${configMap['appId']}\narea:\n${configMap['area']}\npath:\n${configMap['path']}\ncustomUrl:\n${configMap['customUrl']}\noptions:\n${configMap['options']}'); + return showCupertinoAlertDialog(context: context, title: '通知', content: """检测通过,您的配置信息为: +secretId: +${configMap['secretId']} +secretKey: +${configMap['secretKey']} +bucket: +${configMap['bucket']} +appId: +${configMap['appId']} +area: +${configMap['area']} +path: +${configMap['path']} +customUrl: +${configMap['customUrl']} +options: +${configMap['options']} +"""); } } else { if (context.mounted) { @@ -511,7 +450,7 @@ class TencentConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_tencent_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_tencent_config.txt')); } Future get _localPath async { @@ -520,37 +459,17 @@ class TencentConfigState extends State { } Future readTencentConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'TencentConfigPage', - methodName: 'readTencentConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('tencent'); - await Global.setShowedPBhost('tencent'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置腾讯云为默认图床'); - } catch (e) { - FLog.error( - className: 'TencentConfigPage', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('tencent'); + await Global.setShowedPBhost('tencent'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置腾讯云为默认图床'); } } diff --git a/lib/picture_host_configure/configure_page/upyun_configure.dart b/lib/picture_host_configure/configure_page/upyun_configure.dart index 73d7605..a5490db 100644 --- a/lib/picture_host_configure/configure_page/upyun_configure.dart +++ b/lib/picture_host_configure/configure_page/upyun_configure.dart @@ -1,12 +1,10 @@ import 'dart:io'; import 'dart:convert'; - -import 'package:crypto/crypto.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:f_logs/f_logs.dart'; import 'package:fluro/fluro.dart'; +import 'package:horopic/api/upyun_api.dart'; import 'package:path_provider/path_provider.dart'; import 'package:horopic/router/application.dart'; @@ -32,6 +30,8 @@ class UpyunConfigState extends State { final _urlController = TextEditingController(); final _optionsController = TextEditingController(); final _pathController = TextEditingController(); + final _antiLeechTokenController = TextEditingController(); + final _antiLeechExpirationController = TextEditingController(); @override void initState() { @@ -42,20 +42,30 @@ class UpyunConfigState extends State { _initConfig() async { try { Map configMap = await UpyunManageAPI.getConfigMap(); - _bucketController.text = configMap['bucket']; - _operatorController.text = configMap['operator']; - _passwordController.text = configMap['password']; - _urlController.text = configMap['url']; + _bucketController.text = configMap['bucket'] ?? ''; + _operatorController.text = configMap['operator'] ?? ''; + _passwordController.text = configMap['password'] ?? ''; + _urlController.text = configMap['url'] ?? ''; if (configMap['options'] != 'None' || configMap['options'].trim() != '') { - _optionsController.text = configMap['options']; + _optionsController.text = configMap['options'] ?? ''; } else { _optionsController.clear(); } if (configMap['path'] != 'None') { - _pathController.text = configMap['path']; + _pathController.text = configMap['path'] ?? ''; } else { _pathController.clear(); } + if (configMap['antiLeechToken'] != 'None') { + _antiLeechTokenController.text = configMap['antiLeechToken'] ?? ''; + } else { + _antiLeechTokenController.clear(); + } + if (configMap['antiLeechExpiration'] != 'None') { + _antiLeechExpirationController.text = configMap['antiLeechExpiration'] ?? ''; + } else { + _antiLeechExpirationController.clear(); + } } catch (e) { FLog.error( className: 'UpyunConfigState', @@ -73,6 +83,8 @@ class UpyunConfigState extends State { _urlController.dispose(); _optionsController.dispose(); _pathController.dispose(); + _antiLeechTokenController.dispose(); + _antiLeechExpirationController.dispose(); super.dispose(); } @@ -107,7 +119,7 @@ class UpyunConfigState extends State { ), textAlign: TextAlign.center, validator: (value) { - if (value == null || value.isEmpty) { + if (value == null || value.isEmpty || value.trim() == '') { return '请输入bucket'; } return null; @@ -176,6 +188,26 @@ class UpyunConfigState extends State { ), textAlign: TextAlign.center, ), + TextFormField( + controller: _antiLeechTokenController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + label: Center(child: Text('可选: 防盗链Token')), + hintText: '例如abc', + hintStyle: TextStyle(fontSize: 13), + ), + textAlign: TextAlign.center, + ), + TextFormField( + controller: _antiLeechExpirationController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + label: Center(child: Text('可选: 防盗链过期时间')), + hintText: '例如3600,单位秒', + hintStyle: TextStyle(fontSize: 13), + ), + textAlign: TextAlign.center, + ), ListTile( title: ElevatedButton( onPressed: () { @@ -243,9 +275,11 @@ class UpyunConfigState extends State { String url = _urlController.text; String options = _optionsController.text; String path = _pathController.text; + String antiLeechToken = _antiLeechTokenController.text; + String antiLeechExpiration = _antiLeechExpirationController.text; //格式化路径为以/结尾,不以/开头 - if (path.isEmpty || path.replaceAll(' ', '').isEmpty) { + if (path.isEmpty || path.replaceAll(' ', '').isEmpty || path == '/') { path = 'None'; } else { if (!path.endsWith('/')) { @@ -263,81 +297,12 @@ class UpyunConfigState extends State { url = url.substring(0, url.length - 1); } - //save asset image to app dir - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String key = 'PicHoroValidate.jpeg'; - String host = 'http://v0.api.upyun.com'; - String urlpath = ''; - - if (path != 'None') { - urlpath = '/$path$key'; - } else { - urlpath = '/$key'; - } - String date = HttpDate.format(DateTime.now()); - String assetFileMd5 = await assetFile.readAsBytes().then((value) { - return md5.convert(value).toString(); - }); - Map uploadPolicy = { - 'bucket': bucket, - 'save-key': urlpath, - 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, - 'date': date, - 'content-md5': assetFileMd5, - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String stringToSign = 'POST&/$bucket&$date&$base64Policy&$assetFileMd5'; - String passwordMd5 = md5.convert(utf8.encode(password)).toString(); - String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); - String authorization = 'UPYUN $upyunOperator:$signature'; - FormData formData = FormData.fromMap({ - 'authorization': authorization, - 'policy': base64Policy, - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': 'v0.api.upyun.com', - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - 'Date': date, - 'Authorization': authorization, - 'Content-MD5': assetFileMd5, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - '$host/$bucket', - data: formData, - ); - - if (response.statusCode == 200) { - final upyunConfig = UpyunConfigModel(bucket, upyunOperator, password, url, options, path); - final upyunConfigJson = jsonEncode(upyunConfig); - final upyunConfigFile = await localFile; - await upyunConfigFile.writeAsString(upyunConfigJson); - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '成功', content: '配置成功'); - } - } else { - if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '错误', content: '验证失败'); - } - } + final upyunConfig = + UpyunConfigModel(bucket, upyunOperator, password, url, options, path, antiLeechToken, antiLeechExpiration); + final upyunConfigJson = jsonEncode(upyunConfig); + final upyunConfigFile = await localFile; + await upyunConfigFile.writeAsString(upyunConfigJson); + showToast('保存成功'); } catch (e) { FLog.error( className: 'UpyunConfigPageState', @@ -352,10 +317,9 @@ class UpyunConfigState extends State { checkUpyunConfig() async { try { - final upyunConfigFile = await localFile; - String configData = await upyunConfigFile.readAsString(); + final configData = await readUpyunConfig(); - if (configData == "Error") { + if (configData == '') { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -378,60 +342,27 @@ class UpyunConfigState extends State { await assetFile.writeAsBytes(bytes); } String key = 'PicHoroValidate.jpeg'; - String host = 'http://v0.api.upyun.com'; - String urlpath = ''; - if (configMap['path'] != 'None') { - urlpath = '${configMap['path']}$key'; - } else { - urlpath = key; - } - String date = HttpDate.format(DateTime.now()); - String assetFileMd5 = await assetFile.readAsBytes().then((value) { - return md5.convert(value).toString(); - }); - Map uploadPolicy = { - 'bucket': configMap['bucket'], - 'save-key': urlpath, - 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, - 'date': date, - 'content-md5': assetFileMd5, - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String stringToSign = "POST&/${configMap['bucket']}&$date&$base64Policy&$assetFileMd5"; - String passwordMd5 = md5.convert(utf8.encode(configMap['password'])).toString(); - String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); - String authorization = 'UPYUN ${configMap['operator']}:$signature'; - FormData formData = FormData.fromMap({ - 'authorization': authorization, - 'policy': base64Policy, - 'file': await MultipartFile.fromFile(assetFilePath, filename: key), - }); - - BaseOptions baseoptions = setBaseOptions(); - String contentLength = await assetFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': 'v0.api.upyun.com', - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - 'Date': date, - 'Authorization': authorization, - 'Content-MD5': assetFileMd5, - }; - Dio dio = Dio(baseoptions); - var response = await dio.post( - '$host/${configMap['bucket']}', - data: formData, - ); - - if (response.statusCode == 200) { + var checkResult = await UpyunImageUploadUtils.uploadApi(path: assetFilePath, name: key, configMap: configMap); + if (checkResult[0] == 'success') { if (context.mounted) { - return showCupertinoAlertDialog( - context: context, - title: '通知', - content: - '检测通过,您的配置信息为:\nBucket:\n${configMap['bucket']}\nOperator:\n${configMap['operator']}\nPassword:\n${configMap['password']}\nUrl:\n${configMap['url']}\nOptions:\n${configMap['options']}\nPath:\n${configMap['path']}'); + return showCupertinoAlertDialog(context: context, title: '通知', content: """检测通过,您的配置信息为: +Bucket: +${configMap['bucket']} +Operator: +${configMap['operator']} +Password: +${configMap['password']} +Url: +${configMap['url']} +Options: +${configMap['options']} +Path: +${configMap['path']} +AntiLeechToken: +${configMap['antiLeechToken']} +AntiLeechExpiration: +${configMap['antiLeechExpiration']} +"""); } } else { if (context.mounted) { @@ -450,49 +381,29 @@ class UpyunConfigState extends State { } } - Future get localFile async { - final path = await _localPath; - String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_upyun_config.txt'); - } - Future get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } + Future get localFile async { + final path = await _localPath; + String defaultUser = await Global.getUser(); + return ensureFileExists(File('$path/${defaultUser}_upyun_config.txt')); + } + Future readUpyunConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'UpyunConfigPageState', - methodName: 'readUpyunConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { - try { - await Global.setPShost('upyun'); - await Global.setShowedPBhost('upyun'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - showToast('已设置又拍云为默认图床'); - } catch (e) { - FLog.error( - className: 'UpyunConfigPageState', - methodName: '_setdefault', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - if (context.mounted) { - showToastWithContext(context, '错误'); - } - } + await Global.setPShost('upyun'); + await Global.setShowedPBhost('upyun'); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + showToast('已设置又拍云为默认图床'); } } @@ -503,8 +414,11 @@ class UpyunConfigModel { final String url; final String options; final String path; + final String antiLeechToken; + final String antiLeechExpiration; - UpyunConfigModel(this.bucket, this.upyunoperator, this.password, this.url, this.options, this.path); + UpyunConfigModel(this.bucket, this.upyunoperator, this.password, this.url, this.options, this.path, + this.antiLeechToken, this.antiLeechExpiration); Map toJson() => { 'bucket': bucket, @@ -513,6 +427,8 @@ class UpyunConfigModel { 'url': url, 'options': options, 'path': path, + 'antiLeechToken': antiLeechToken, + 'antiLeechExpiration': antiLeechExpiration, }; static List keysList = [ @@ -523,5 +439,7 @@ class UpyunConfigModel { 'url', 'options', 'path', + 'antiLeechToken', + 'antiLeechExpiration', ]; } diff --git a/lib/picture_host_configure/configure_page/webdav_configure.dart b/lib/picture_host_configure/configure_page/webdav_configure.dart index b6c2662..387f14f 100644 --- a/lib/picture_host_configure/configure_page/webdav_configure.dart +++ b/lib/picture_host_configure/configure_page/webdav_configure.dart @@ -39,9 +39,9 @@ class WebdavConfigState extends State { _initConfig() async { try { Map configMap = await WebdavManageAPI.getConfigMap(); - _hostController.text = configMap['host']; - _usernameController.text = configMap['webdavusername']; - _passwdController.text = configMap['password']; + _hostController.text = configMap['host'] ?? ''; + _usernameController.text = configMap['webdavusername'] ?? ''; + _passwdController.text = configMap['password'] ?? ''; if (configMap['uploadPath'] != 'None' && configMap['uploadPath'] != null) { _uploadPathController.text = configMap['uploadPath']; } else { @@ -134,7 +134,6 @@ class WebdavConfigState extends State { ), TextFormField( controller: _passwdController, - obscureText: true, decoration: const InputDecoration( label: Center(child: Text('密码')), hintText: '输入密码', @@ -275,30 +274,12 @@ class WebdavConfigState extends State { } try { - var client = webdav.newClient( - host, - user: username, - password: password, - ); - client.setHeaders({'accept-charset': 'utf-8'}); - client.setConnectTimeout(30000); - client.setSendTimeout(30000); - client.setReceiveTimeout(30000); - await client.ping(); - final webdavConfig = WebdavConfigModel(host, username, password, uploadPath, customUrl, webPath); final webdavConfigJson = jsonEncode(webdavConfig); final webdavConfigFile = await localFile; webdavConfigFile.writeAsString(webdavConfigJson); setState(() {}); - if (context.mounted) { - return showCupertinoAlertDialog( - context: context, - barrierDismissible: false, - title: '配置成功', - content: '配置成功!', - ); - } + showToast('保存成功'); } catch (e) { FLog.error( className: 'WebdavConfigPage', @@ -306,16 +287,15 @@ class WebdavConfigState extends State { text: formatErrorMessage({}, e.toString()), dataLogType: DataLogType.ERRORS.toString()); if (context.mounted) { - return showCupertinoAlertDialog(context: context, title: '连接webdav服务失败', content: e.toString()); + return showCupertinoAlertDialog(context: context, title: '错误', content: e.toString()); } } } checkWebdavConfig() async { try { - final webdavConfigFile = await localFile; - String configData = await webdavConfigFile.readAsString(); - if (configData == "Error") { + final configData = await readWebdavConfig(); + if (configData == "") { if (context.mounted) { return showCupertinoAlertDialog(context: context, title: "检查失败!", content: "请先配置上传参数."); } @@ -333,11 +313,20 @@ class WebdavConfigState extends State { client.setReceiveTimeout(30000); await client.ping(); if (context.mounted) { - return showCupertinoAlertDialog( - context: context, - title: '通知', - content: - '检测通过,您的配置信息为:\nhost:\n${configMap["host"]}\nwebdav用户名:\n${configMap["webdavusername"]}\n密码:\n${configMap["password"]}\nuploadPath:\n${configMap["uploadPath"]}\n自定义域名:\n${configMap["customUrl"]}\nwebPath:\n${configMap["webPath"]}'); + return showCupertinoAlertDialog(context: context, title: '通知', content: """检测通过,您的配置信息为: +host: +${configMap["host"]} +webdav用户名: +${configMap["webdavusername"]} +密码: +${configMap["password"]} +uploadPath: +${configMap["uploadPath"]} +自定义域名: +${configMap["customUrl"]} +webPath: +${configMap["webPath"]} +"""); } } catch (e) { FLog.error( @@ -354,7 +343,7 @@ class WebdavConfigState extends State { Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_webdav_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_webdav_config.txt')); } Future get _localPath async { @@ -363,18 +352,9 @@ class WebdavConfigState extends State { } Future readWebdavConfig() async { - try { - final file = await localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - FLog.error( - className: 'WebdavConfigPage', - methodName: 'readWebdavConfig', - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - return "Error"; - } + final file = await localFile; + String contents = await file.readAsString(); + return contents; } _setdefault() async { diff --git a/lib/picture_host_configure/configure_store/configure_store_edit_page/upyun_configure_store_edit.dart b/lib/picture_host_configure/configure_store/configure_store_edit_page/upyun_configure_store_edit.dart index c831d43..c603d2a 100644 --- a/lib/picture_host_configure/configure_store/configure_store_edit_page/upyun_configure_store_edit.dart +++ b/lib/picture_host_configure/configure_store/configure_store_edit_page/upyun_configure_store_edit.dart @@ -29,6 +29,8 @@ class UpyunConfigureStoreEditState extends State { final _urlController = TextEditingController(); final _optionsController = TextEditingController(); final _pathController = TextEditingController(); + final _antiLeechTokenController = TextEditingController(); + final _antiLeechExpirationController = TextEditingController(); @override void initState() { @@ -45,6 +47,8 @@ class UpyunConfigureStoreEditState extends State { 'url', 'options', 'path', + 'antiLeechToken', + 'antiLeechExpiration', ]; for (String element in keys) { if (widget.psInfo[element] != ConfigureTemplate.placeholder) { @@ -70,6 +74,12 @@ class UpyunConfigureStoreEditState extends State { case 'path': _pathController.text = widget.psInfo[element]; break; + case 'antiLeechToken': + _antiLeechTokenController.text = widget.psInfo[element]; + break; + case 'antiLeechExpiration': + _antiLeechExpirationController.text = widget.psInfo[element]; + break; } } } @@ -84,6 +94,8 @@ class UpyunConfigureStoreEditState extends State { _urlController.dispose(); _optionsController.dispose(); _pathController.dispose(); + _antiLeechTokenController.dispose(); + _antiLeechExpirationController.dispose(); super.dispose(); } @@ -184,6 +196,26 @@ class UpyunConfigureStoreEditState extends State { ), textAlign: TextAlign.center, ), + TextFormField( + controller: _antiLeechTokenController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + label: Center(child: Text('可选: 防盗链Token')), + hintText: '例如abc', + hintStyle: TextStyle(fontSize: 13), + ), + textAlign: TextAlign.center, + ), + TextFormField( + controller: _antiLeechExpirationController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + label: Center(child: Text('可选: 防盗链过期时间')), + hintText: '例如3600,单位秒', + hintStyle: TextStyle(fontSize: 13), + ), + textAlign: TextAlign.center, + ), ListTile( title: ElevatedButton( onPressed: () { @@ -211,17 +243,25 @@ class UpyunConfigureStoreEditState extends State { _importConfig() async { try { Map configMap = await UpyunManageAPI.getConfigMap(); - _bucketController.text = configMap['bucket']; - _operatorController.text = configMap['operator']; - _passwordController.text = configMap['password']; - _urlController.text = configMap['url']; - if (configMap['path'] != 'None') { + _bucketController.text = configMap['bucket'] ?? ''; + _operatorController.text = configMap['operator'] ?? ''; + _passwordController.text = configMap['password'] ?? ''; + _urlController.text = configMap['url'] ?? ''; + if (configMap['path'] != 'None' && configMap['path'] != null && configMap['path'] != '/') { _pathController.text = configMap['path']; } if (configMap['options'] != 'None' || configMap['options'].toString().trim() != '') { _optionsController.text = configMap['options']; } + + if (configMap['antiLeechToken'] != 'None' || configMap['antiLeechToken'].toString().trim() != '') { + _antiLeechTokenController.text = configMap['antiLeechToken']; + } + + if (configMap['antiLeechExpiration'] != 'None' || configMap['antiLeechExpiration'].toString().trim() != '') { + _antiLeechExpirationController.text = configMap['antiLeechExpiration']; + } showToast('导入成功'); } catch (e) { showToast('导入失败'); @@ -237,6 +277,8 @@ class UpyunConfigureStoreEditState extends State { String url = _urlController.text; String options = _optionsController.text; String path = _pathController.text; + String antiLeechToken = _antiLeechTokenController.text; + String antiLeechExpiration = _antiLeechExpirationController.text; if (remarkName.isEmpty || remarkName.trim().isEmpty) { remarkName = ConfigureTemplate.placeholder; @@ -268,6 +310,8 @@ class UpyunConfigureStoreEditState extends State { 'url': url, 'options': options, 'path': path, + 'antiLeechToken': antiLeechToken, + 'antiLeechExpiration': antiLeechExpiration, }; try { diff --git a/lib/picture_host_configure/configure_store/configure_store_edit_page/webdav_configure_store_edit.dart b/lib/picture_host_configure/configure_store/configure_store_edit_page/webdav_configure_store_edit.dart index 93fb4d4..52523eb 100644 --- a/lib/picture_host_configure/configure_store/configure_store_edit_page/webdav_configure_store_edit.dart +++ b/lib/picture_host_configure/configure_store/configure_store_edit_page/webdav_configure_store_edit.dart @@ -140,7 +140,6 @@ class WebdavConfigureStoreEditState extends State { ), TextFormField( controller: _passwordController, - obscureText: true, decoration: const InputDecoration( label: Center(child: Text('密码')), hintText: '输入密码', diff --git a/lib/picture_host_configure/configure_store/configure_store_page.dart b/lib/picture_host_configure/configure_store/configure_store_page.dart index 78df7a2..9ec3dd1 100644 --- a/lib/picture_host_configure/configure_store/configure_store_page.dart +++ b/lib/picture_host_configure/configure_store/configure_store_page.dart @@ -705,6 +705,8 @@ class ConfigureStorePageState extends State { String url = psInfo['url']!; String options = psInfo['options']!; String path = psInfo['path']!; + String antiLeechToken = psInfo['antiLeechToken']!; + String antiLeechType = psInfo['antiLeechType']!; bool valid = validateUndetermined([ bucket, @@ -720,7 +722,8 @@ class ConfigureStorePageState extends State { path = 'None'; } - final upyunConfig = UpyunConfigModel(bucket, operator, password, url, options, path); + final upyunConfig = + UpyunConfigModel(bucket, operator, password, url, options, path, antiLeechToken, antiLeechType); final upyunConfigJson = jsonEncode(upyunConfig); final upyunConfigFile = await UpyunConfigState().localFile; await upyunConfigFile.writeAsString(upyunConfigJson); diff --git a/lib/picture_host_configure/default_picture_host_select.dart b/lib/picture_host_configure/default_picture_host_select.dart index 03560da..4a36efc 100644 --- a/lib/picture_host_configure/default_picture_host_select.dart +++ b/lib/picture_host_configure/default_picture_host_select.dart @@ -42,161 +42,65 @@ class DefaultPShostSelectState extends State { title: titleText('默认图床选择'), ), body: ListView( - children: [ - ListTile( - title: const Text('Alist V3'), - trailing: Global.defaultPShost == 'alist' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('alist'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('阿里云'), - trailing: Global.defaultPShost == 'aliyun' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('aliyun'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('FTP-SSH/SFTP'), - trailing: Global.defaultPShost == 'ftp' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('ftp'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('Github图床'), - trailing: Global.defaultPShost == 'github' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('github'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('Imgur图床'), - trailing: Global.defaultPShost == 'imgur' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('imgur'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('兰空图床'), - trailing: Global.defaultPShost == 'lsky.pro' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('lsky.pro'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('七牛云'), - trailing: Global.defaultPShost == 'qiniu' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('qiniu'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('S3兼容平台'), - trailing: Global.defaultPShost == 'aws' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('aws'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('SM.MS'), - trailing: Global.defaultPShost == 'sm.ms' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('sm.ms'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('腾讯云'), - trailing: Global.defaultPShost == 'tencent' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('tencent'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('又拍云'), - trailing: Global.defaultPShost == 'upyun' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('upyun'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ListTile( - title: const Text('WebDAV'), - trailing: Global.defaultPShost == 'webdav' ? const Icon(Icons.check) : null, - onTap: () async { - await setdefaultPShostRemoteAndLocal('webdav'); - eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); - eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); - setState(() {}); - }, - ), - ], + children: _buildHostTiles(), ), ); } + + List _buildHostTiles() { + final hosts = [ + {'name': 'Alist V3', 'id': 'alist'}, + {'name': '阿里云', 'id': 'aliyun'}, + {'name': 'FTP-SSH/SFTP', 'id': 'ftp'}, + {'name': 'Github图床', 'id': 'github'}, + {'name': 'Imgur图床', 'id': 'imgur'}, + {'name': '兰空图床', 'id': 'lsky.pro'}, + {'name': '七牛云', 'id': 'qiniu'}, + {'name': 'S3兼容平台', 'id': 'aws'}, + {'name': 'SM.MS', 'id': 'sm.ms'}, + {'name': '腾讯云', 'id': 'tencent'}, + {'name': '又拍云', 'id': 'upyun'}, + {'name': 'WebDAV', 'id': 'webdav'}, + ]; + + return hosts.map((host) { + return ListTile( + title: Text(host['name']!), + trailing: Global.defaultPShost == host['id'] ? const Icon(Icons.check) : null, + onTap: () async { + await setdefaultPShostRemoteAndLocal(host['id']!); + eventBus.fire(AlbumRefreshEvent(albumKeepAlive: false)); + eventBus.fire(HomePhotoRefreshEvent(homePhotoKeepAlive: false)); + setState(() {}); + }, + ); + }).toList(); + } } setdefaultPShostRemoteAndLocal(String psHost) async { try { await Global.setPShost(psHost); - if (psHost == 'lsky.pro') { - await Global.setShowedPBhost('lskypro'); - } else if (psHost == 'sm.ms') { - await Global.setShowedPBhost('smms'); - } else if (psHost == 'github') { - await Global.setShowedPBhost('github'); - } else if (psHost == 'imgur') { - await Global.setShowedPBhost('imgur'); - } else if (psHost == 'qiniu') { - await Global.setShowedPBhost('qiniu'); - } else if (psHost == 'tencent') { - await Global.setShowedPBhost('tencent'); - } else if (psHost == 'aliyun') { - await Global.setShowedPBhost('aliyun'); - } else if (psHost == 'upyun') { - await Global.setShowedPBhost('upyun'); - } else if (psHost == 'ftp') { - await Global.setShowedPBhost('PBhostExtend1'); - } else if (psHost == 'aws') { - await Global.setShowedPBhost('PBhostExtend2'); - } else if (psHost == 'alist') { - await Global.setShowedPBhost('PBhostExtend3'); - } else if (psHost == 'webdav') { - await Global.setShowedPBhost('PBhostExtend4'); + + Map hostMapping = { + 'lsky.pro': 'lskypro', + 'sm.ms': 'smms', + 'github': 'github', + 'imgur': 'imgur', + 'qiniu': 'qiniu', + 'tencent': 'tencent', + 'aliyun': 'aliyun', + 'upyun': 'upyun', + 'ftp': 'PBhostExtend1', + 'aws': 'PBhostExtend2', + 'alist': 'PBhostExtend3', + 'webdav': 'PBhostExtend4', + }; + + if (hostMapping.containsKey(psHost)) { + await Global.setShowedPBhost(hostMapping[psHost]!); } + showToast('已设置$psHost为默认图床'); } catch (e) { FLog.error( diff --git a/lib/picture_host_manage/alist/alist_download_manage_page.dart b/lib/picture_host_manage/alist/alist_download_manage_page.dart index 6c7e82a..dbbd2e8 100644 --- a/lib/picture_host_manage/alist/alist_download_manage_page.dart +++ b/lib/picture_host_manage/alist/alist_download_manage_page.dart @@ -9,7 +9,7 @@ import 'package:fluro/fluro.dart'; import 'package:horopic/picture_host_manage/alist/download_api/alist_downloader.dart'; import 'package:horopic/picture_host_manage/alist/download_api/alist_download_task.dart'; -import 'package:horopic/picture_host_manage/tencent/download_api/download_status.dart'; +import 'package:horopic/picture_host_manage/common_page/download/pnc_download_status.dart'; import 'package:horopic/picture_host_manage/alist/upload_api/alist_upload_utils.dart'; import 'package:horopic/pages/upload_pages/upload_status.dart'; import 'package:horopic/picture_host_manage/alist/upload_api/alist_upload_task.dart'; diff --git a/lib/picture_host_manage/alist/download_api/alist_download_task.dart b/lib/picture_host_manage/alist/download_api/alist_download_task.dart index 5c50501..bda06a8 100644 --- a/lib/picture_host_manage/alist/download_api/alist_download_task.dart +++ b/lib/picture_host_manage/alist/download_api/alist_download_task.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:f_logs/f_logs.dart'; -import 'package:horopic/picture_host_manage/tencent/download_api/download_status.dart'; +import 'package:horopic/picture_host_manage/common_page/download/pnc_download_status.dart'; import 'package:horopic/picture_host_manage/alist/download_api/alist_download_request.dart'; import 'package:horopic/utils/common_functions.dart'; diff --git a/lib/picture_host_manage/alist/download_api/alist_downloader.dart b/lib/picture_host_manage/alist/download_api/alist_downloader.dart index 328ccc3..a825685 100644 --- a/lib/picture_host_manage/alist/download_api/alist_downloader.dart +++ b/lib/picture_host_manage/alist/download_api/alist_downloader.dart @@ -7,10 +7,9 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:horopic/picture_host_manage/alist/download_api/alist_download_task.dart'; -import 'package:horopic/picture_host_manage/tencent/download_api/download_status.dart'; +import 'package:horopic/picture_host_manage/common_page/download/pnc_download_status.dart'; import 'package:horopic/picture_host_manage/alist/download_api/alist_download_request.dart'; import 'package:horopic/utils/common_functions.dart'; @@ -132,34 +131,17 @@ class DownloadManager { } } } catch (e) { - if (e is DioException) { - FLog.error( - className: 'alist_DownloadManager', - methodName: 'download', - text: formatErrorMessage({ - 'url': url, - 'savePath': savePath, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: 'alist_DownloadManager', - methodName: 'download', - text: formatErrorMessage({ - 'url': url, - 'savePath': savePath, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'url': url, + 'savePath': savePath, + }, + 'alist_DownloadManager', + 'download'); var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/alist/upload_api/alist_upload_utils.dart b/lib/picture_host_manage/alist/upload_api/alist_upload_utils.dart index 8fa6b47..0537a5b 100644 --- a/lib/picture_host_manage/alist/upload_api/alist_upload_utils.dart +++ b/lib/picture_host_manage/alist/upload_api/alist_upload_utils.dart @@ -102,11 +102,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_manage/aliyun/download_api/aliyun_downloader.dart b/lib/picture_host_manage/aliyun/download_api/aliyun_downloader.dart index 4512812..383901a 100644 --- a/lib/picture_host_manage/aliyun/download_api/aliyun_downloader.dart +++ b/lib/picture_host_manage/aliyun/download_api/aliyun_downloader.dart @@ -133,12 +133,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/aliyun/upload_api/aliyun_upload_utils.dart b/lib/picture_host_manage/aliyun/upload_api/aliyun_upload_utils.dart index f2b9301..8252c12 100644 --- a/lib/picture_host_manage/aliyun/upload_api/aliyun_upload_utils.dart +++ b/lib/picture_host_manage/aliyun/upload_api/aliyun_upload_utils.dart @@ -129,11 +129,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_manage/aws/download_api/aws_downloader.dart b/lib/picture_host_manage/aws/download_api/aws_downloader.dart index 320eca9..0780458 100644 --- a/lib/picture_host_manage/aws/download_api/aws_downloader.dart +++ b/lib/picture_host_manage/aws/download_api/aws_downloader.dart @@ -7,7 +7,6 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:f_logs/f_logs.dart'; import 'package:minio/minio.dart'; import 'package:horopic/picture_host_manage/common_page/download/pnc_download_task.dart'; @@ -153,34 +152,17 @@ class DownloadManager { } } } catch (e) { - if (e is DioException) { - FLog.error( - className: 'tencent_DownloadManager', - methodName: 'download', - text: formatErrorMessage({ - 'url': url, - 'savePath': savePath, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: 'tencent_DownloadManager', - methodName: 'download', - text: formatErrorMessage({ - 'url': url, - 'savePath': savePath, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'url': url, + 'savePath': savePath, + }, + 'aws_DownloadManager', + 'download'); var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/aws/upload_api/aws_upload_utils.dart b/lib/picture_host_manage/aws/upload_api/aws_upload_utils.dart index f7f7d90..a81e982 100644 --- a/lib/picture_host_manage/aws/upload_api/aws_upload_utils.dart +++ b/lib/picture_host_manage/aws/upload_api/aws_upload_utils.dart @@ -106,11 +106,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_manage/common_page/base_download_manage_page.dart b/lib/picture_host_manage/common_page/base_download_manage_page.dart index c791fbc..6f1d28d 100644 --- a/lib/picture_host_manage/common_page/base_download_manage_page.dart +++ b/lib/picture_host_manage/common_page/base_download_manage_page.dart @@ -251,7 +251,7 @@ class BaseUpDownloadManagePageState extends State { child: UploadListItem( onUploadPlayPausedPressed: (path, fileName, configMap) async { var task = uploadManager.getUpload(jsonDecode(currentUploadList[i])[1]); - if (task != null && !task.status.isCompleted) { + if (task != null && !task.status.value.isCompleted) { switch (task.status.value) { case UploadStatus.uploading: await uploadManager.pauseUpload(path, fileName); @@ -355,7 +355,10 @@ class BaseUpDownloadManagePageState extends State { child: ListItem( onDownloadPlayPausedPressed: (url) async { var task = downloadManager.getDownload(currentDownloadList[i]); - if (task != null && !task.status.isCompleted) { + if (task != null && + task.status.value != DownloadStatus.completed && + task.status.value != DownloadStatus.failed && + task.status.value != DownloadStatus.canceled) { switch (task.status.value) { case DownloadStatus.downloading: await downloadManager.pauseDownload(url); diff --git a/lib/picture_host_manage/common_page/file_explorer/local_image_preview.dart b/lib/picture_host_manage/common_page/file_explorer/local_image_preview.dart index a2fa4bd..09f0aae 100644 --- a/lib/picture_host_manage/common_page/file_explorer/local_image_preview.dart +++ b/lib/picture_host_manage/common_page/file_explorer/local_image_preview.dart @@ -1,4 +1,4 @@ -import 'dart:io'; +import 'package:universal_io/io.dart'; import 'package:flutter/material.dart'; import 'package:f_logs/f_logs.dart'; diff --git a/lib/picture_host_manage/common_page/picture_host_manage_entry.dart b/lib/picture_host_manage/common_page/picture_host_manage_entry.dart index 4268de1..01c2e28 100644 --- a/lib/picture_host_manage/common_page/picture_host_manage_entry.dart +++ b/lib/picture_host_manage/common_page/picture_host_manage_entry.dart @@ -211,7 +211,7 @@ class PsHostHomePageState extends State with AutomaticKeepAliveC child: InkWell( onTap: () async { var queryUpyunManage = await UpyunManageAPI.readUpyunManageConfig(); - if (queryUpyunManage == 'Error') { + if (queryUpyunManage == 'Error' || queryUpyunManage == '') { if (mounted) { Application.router.navigateTo( context, diff --git a/lib/picture_host_manage/ftp/download_api/sftp_downloader.dart b/lib/picture_host_manage/ftp/download_api/sftp_downloader.dart index e483da2..ab6a374 100644 --- a/lib/picture_host_manage/ftp/download_api/sftp_downloader.dart +++ b/lib/picture_host_manage/ftp/download_api/sftp_downloader.dart @@ -150,12 +150,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/ftp/upload_api/sftp_upload_utils.dart b/lib/picture_host_manage/ftp/upload_api/sftp_upload_utils.dart index c3093f8..0781680 100644 --- a/lib/picture_host_manage/ftp/upload_api/sftp_upload_utils.dart +++ b/lib/picture_host_manage/ftp/upload_api/sftp_upload_utils.dart @@ -100,11 +100,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_manage/github/download_api/github_downloader.dart b/lib/picture_host_manage/github/download_api/github_downloader.dart index f687662..ed03d70 100644 --- a/lib/picture_host_manage/github/download_api/github_downloader.dart +++ b/lib/picture_host_manage/github/download_api/github_downloader.dart @@ -113,12 +113,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/github/upload_api/github_upload_utils.dart b/lib/picture_host_manage/github/upload_api/github_upload_utils.dart index 012730e..297d5c3 100644 --- a/lib/picture_host_manage/github/upload_api/github_upload_utils.dart +++ b/lib/picture_host_manage/github/upload_api/github_upload_utils.dart @@ -99,11 +99,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/picture_host_manage/imgur/download_api/imgur_downloader.dart b/lib/picture_host_manage/imgur/download_api/imgur_downloader.dart index 4626dd7..8f5eb63 100644 --- a/lib/picture_host_manage/imgur/download_api/imgur_downloader.dart +++ b/lib/picture_host_manage/imgur/download_api/imgur_downloader.dart @@ -110,12 +110,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/imgur/imgur_file_explorer.dart b/lib/picture_host_manage/imgur/imgur_file_explorer.dart index d08e33b..eb259c2 100644 --- a/lib/picture_host_manage/imgur/imgur_file_explorer.dart +++ b/lib/picture_host_manage/imgur/imgur_file_explorer.dart @@ -772,7 +772,7 @@ class ImgurFileExplorerState extends loading_state.BaseLoadingPageState get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_alist_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_alist_config.txt')); } static Future get _localPath async { @@ -88,6 +88,9 @@ class AlistManageAPI { static Future getConfigMap() async { String configStr = await readAlistConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -101,19 +104,19 @@ class AlistManageAPI { } static getToken(String host, String username, String password) async { - String url = '$host/api/auth/login'; - Map queryParameters = { - 'Password': password, - 'Username': username, - }; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Content-Type": "application/json", - }; - Dio dio = Dio(baseoptions); - try { + String url = '$host/api/auth/login'; + Map queryParameters = { + 'Password': password, + 'Username': username, + }; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Content-Type": "application/json", + }; + Dio dio = Dio(baseoptions); + var response = await dio.post(url, data: queryParameters); if (response.statusCode == 200 && response.data['message'] == 'success') { return ['success', response.data['data']['token']]; @@ -121,23 +124,13 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "getToken", - text: formatErrorMessage({ - 'host': host, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "getToken", - text: formatErrorMessage({ - 'host': host, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'host': host, + }, + "AlistManageAPI", + "getToken"); return [e.toString()]; } } @@ -167,18 +160,18 @@ class AlistManageAPI { } static getBucketList() async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/admin/storage/list'; + try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/admin/storage/list'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - }; - Dio dio = Dio(baseoptions); + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + }; + Dio dio = Dio(baseoptions); - try { var response = await dio.get( url, ); @@ -188,39 +181,28 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "getBucketList"); return [e.toString()]; } } static changeBucketState(Map element, bool enable) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String enableUrl = '$host/api/admin/storage/enable'; - String disableUrl = '$host/api/admin/storage/disable'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - }; - Map queryParameters = { - 'id': element['id'], - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String enableUrl = '$host/api/admin/storage/enable'; + String disableUrl = '$host/api/admin/storage/disable'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + }; + Map queryParameters = { + 'id': element['id'], + }; + Dio dio = Dio(baseoptions); + Response response; if (enable) { response = await dio.post( @@ -234,154 +216,101 @@ class AlistManageAPI { ); } if (response.statusCode == 200 && response.data['message'] == 'success') { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "changeBucketState", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "changeBucketState", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "changeBucketState"); return [e.toString()]; } } static deleteBucket(Map element) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/admin/storage/delete'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - }; - Map queryParameters = { - 'id': element['id'], - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/admin/storage/delete'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + }; + Map queryParameters = { + 'id': element['id'], + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, queryParameters: queryParameters, ); if (response.statusCode == 200 && response.data['message'] == 'success') { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "deleteBucket"); return [e.toString()]; } } static createBucket(Map newBucketConfig) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/admin/storage/create'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/admin/storage/create'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: newBucketConfig, ); if (response.statusCode == 200 && response.data['message'] == 'success') { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "createBucket", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "createBucket", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "createBucket"); return [e.toString()]; } } static updateBucket(Map newBucketConfig) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/admin/storage/update'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/admin/storage/update'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: newBucketConfig, ); if (response.statusCode == 200 && response.data['message'] == 'success') { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "updateBucket", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "updateBucket", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "updateBucket"); return [e.toString()]; } } @@ -426,24 +355,25 @@ class AlistManageAPI { String folder, String refresh, ) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/list'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "page": 1, - "path": folder, - "per_page": 1, - "refresh": refresh == 'Refresh' ? true : false, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/list'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "page": 1, + "path": folder, + "per_page": 1, + "refresh": refresh == 'Refresh' ? true : false, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: dataMap, @@ -458,43 +388,32 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "getTotalPage", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "getTotalPage", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "getTotalPage"); return [e.toString()]; } } static listFolderByPage(String folder, String refresh, int page) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/list'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "page": page, - "path": folder, - "per_page": 50, - "refresh": refresh == 'Refresh' ? true : false, - }; - Dio dio = Dio(baseoptions); - List fileList = []; try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/list'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "page": page, + "path": folder, + "per_page": 50, + "refresh": refresh == 'Refresh' ? true : false, + }; + Dio dio = Dio(baseoptions); + List fileList = []; + var response = await dio.post( url, data: dataMap, @@ -510,44 +429,33 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "listFolderByPage", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "listFolderByPage", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "listFolderByPage"); return [e.toString()]; } } static listFolder(String folder, String refresh) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/list'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - int startPage = 1; - Map dataMap = { - "page": startPage, - "path": folder, - "per_page": 1000, - "refresh": refresh == 'Refresh' ? true : false, - }; - Dio dio = Dio(baseoptions); - List fileList = []; try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/list'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + int startPage = 1; + Map dataMap = { + "page": startPage, + "path": folder, + "per_page": 1000, + "refresh": refresh == 'Refresh' ? true : false, + }; + Dio dio = Dio(baseoptions); + List fileList = []; + var response = await dio.post( url, data: dataMap, @@ -582,39 +490,28 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "listFolder", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "listFolder", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "listFolder"); return [e.toString()]; } } static getFileInfo(String path) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/get'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "path": path, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/get'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "path": path, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: dataMap, @@ -625,39 +522,28 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "getFileInfo", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "getFileInfo", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "getFileInfo"); return [e.toString()]; } } static mkDir(String path) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/mkdir'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "path": path, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/mkdir'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "path": path, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: dataMap, @@ -668,40 +554,29 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "mkDir", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "mkDir", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "mkDir"); return [e.toString()]; } } static rename(String source, String target) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/rename'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "path": source, - "name": target, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/rename'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "path": source, + "name": target, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: dataMap, @@ -712,40 +587,29 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "rename", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "rename", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "rename"); return [e.toString()]; } } static remove(String dir, List names) async { - Map configMap = await getConfigMap(); - String host = configMap['host']; - String token = configMap['token']; - String url = '$host/api/fs/remove'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": token, - "Content-Type": "application/json", - }; - Map dataMap = { - "dir": dir, - "names": names, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String host = configMap['host']; + String token = configMap['token']; + String url = '$host/api/fs/remove'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": token, + "Content-Type": "application/json", + }; + Map dataMap = { + "dir": dir, + "names": names, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( url, data: dataMap, @@ -756,54 +620,43 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "remove", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "remove", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "remove"); return [e.toString()]; } } static uploadFile(String filename, String filepath, String uploadPath) async { - Map configMap = await getConfigMap(); - if (uploadPath == 'None') { - uploadPath = '/'; - } else { - if (!uploadPath.startsWith('/')) { - uploadPath = '/$uploadPath'; - } - if (!uploadPath.endsWith('/')) { - uploadPath = '$uploadPath/'; - } - } - String filePath = uploadPath + filename; - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(filepath, filename: filename), - }); - File uploadFile = File(filepath); - int contentLength = await uploadFile.length().then((value) { - return value; - }); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - "Authorization": configMap["token"], - "Content-Type": Global.multipartString, - "file-path": Uri.encodeComponent(filePath), - "Content-Length": contentLength, - }; - Dio dio = Dio(baseoptions); - String uploadUrl = configMap["host"] + "/api/fs/form"; try { + Map configMap = await getConfigMap(); + if (uploadPath == 'None') { + uploadPath = '/'; + } else { + if (!uploadPath.startsWith('/')) { + uploadPath = '/$uploadPath'; + } + if (!uploadPath.endsWith('/')) { + uploadPath = '$uploadPath/'; + } + } + String filePath = uploadPath + filename; + FormData formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(filepath, filename: filename), + }); + File uploadFile = File(filepath); + int contentLength = await uploadFile.length().then((value) { + return value; + }); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + "Authorization": configMap["token"], + "Content-Type": Global.multipartString, + "file-path": Uri.encodeComponent(filePath), + "Content-Length": contentLength, + }; + Dio dio = Dio(baseoptions); + String uploadUrl = configMap["host"] + "/api/fs/form"; + var response = await dio.put( uploadUrl, data: formdata, @@ -814,19 +667,7 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AlistManageAPI", "uploadFile"); return [e.toString()]; } } @@ -857,20 +698,7 @@ class AlistManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AlistManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'uploadPath': uploadPath}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AlistManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'uploadPath': uploadPath}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'fileLink': fileLink, 'uploadPath': uploadPath}, "AlistManageAPI", "uploadNetworkFile"); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/aliyun_manage_api.dart b/lib/picture_host_manage/manage_api/aliyun_manage_api.dart index a302141..87f377a 100644 --- a/lib/picture_host_manage/manage_api/aliyun_manage_api.dart +++ b/lib/picture_host_manage/manage_api/aliyun_manage_api.dart @@ -15,40 +15,41 @@ import 'package:horopic/picture_host_configure/configure_page/aliyun_configure.d class AliyunManageAPI { static Map areaCodeName = { - 'oss-cn-hangzhou': '华东1(杭州)', - 'oss-cn-shanghai': '华东2(上海)', - 'oss-cn-nanjing': '华东5(南京本地地域)', - 'oss-cn-fuzhou': '华东6(福州本地地域)', - 'oss-cn-qingdao': '华北1(青岛)', - 'oss-cn-beijing': '华北2(北京)', - 'oss-cn-zhangjiakou': '华北3(张家口)', - 'oss-cn-huhehaote': '华北5(呼和浩特)', - 'oss-cn-wulanchabu': '华北6(乌兰察布)', - 'oss-cn-shenzhen': '华南1(深圳)', - 'oss-cn-heyuan': '华南2(河源)', - 'oss-cn-guangzhou': '华南3(广州)', - 'oss-cn-chengdu': '西南1(成都)', - 'oss-cn-hongkong': '中国(香港)', - 'oss-us-west-1': '美国(硅谷)', - 'oss-us-east-1': '美国(弗吉尼亚)', - 'oss-ap-northeast-1': '日本(东京)', - 'oss-ap-northeast-2': '韩国(首尔)', + 'oss-cn-hangzhou': '华东1(杭州)', + 'oss-cn-shanghai': '华东2(上海)', + 'oss-cn-nanjing': '华东5(南京本地地域)', + 'oss-cn-fuzhou': '华东6(福州本地地域)', + 'oss-cn-qingdao': '华北1(青岛)', + 'oss-cn-beijing': '华北2(北京)', + 'oss-cn-zhangjiakou': '华北3(张家口)', + 'oss-cn-huhehaote': '华北5(呼和浩特)', + 'oss-cn-wulanchabu': '华北6(乌兰察布)', + 'oss-cn-shenzhen': '华南1(深圳)', + 'oss-cn-heyuan': '华南2(河源)', + 'oss-cn-guangzhou': '华南3(广州)', + 'oss-cn-chengdu': '西南1(成都)', + 'oss-cn-hongkong': '中国(香港)', + 'oss-us-west-1': '美国(硅谷)', + 'oss-us-east-1': '美国(弗吉尼亚)', + 'oss-ap-northeast-1': '日本(东京)', + 'oss-ap-northeast-2': '韩国(首尔)', 'oss-ap-southeast-1': '新加坡', - 'oss-ap-southeast-2': '澳大利亚(悉尼)', - 'oss-ap-southeast-3': '马来西亚(吉隆坡)', - 'oss-ap-southeast-5': '印度尼西亚(雅加达)', - 'oss-ap-southeast-6': '菲律宾(马尼拉)', - 'oss-ap-southeast-7': '泰国(曼谷)', - 'oss-ap-south-1': '印度(孟买)', - 'oss-eu-central-1': '德国(法兰克福)', - 'oss-eu-west-1': '英国(伦敦)', - 'oss-me-east-1': '阿联酋(迪拜)', + 'oss-ap-southeast-2': '澳大利亚(悉尼)', + 'oss-ap-southeast-3': '马来西亚(吉隆坡)', + 'oss-ap-southeast-5': '印度尼西亚(雅加达)', + 'oss-ap-southeast-6': '菲律宾(马尼拉)', + 'oss-ap-southeast-7': '泰国(曼谷)', + 'oss-ap-south-1': '印度(孟买)', + 'oss-eu-central-1': '德国(法兰克福)', + 'oss-eu-west-1': '英国(伦敦)', + 'oss-me-east-1': '阿联酋(迪拜)', + 'oss-rg-china-mainland': '无地域属性' }; static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_aliyun_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_aliyun_config.txt')); } static Future get _localPath async { @@ -73,6 +74,9 @@ class AliyunManageAPI { static Future getConfigMap() async { String configStr = await readAliyunConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -143,29 +147,30 @@ class AliyunManageAPI { 'contentType': contentType, }, e.toString()), dataLogType: DataLogType.ERRORS.toString()); - return ""; + rethrow; } } //获取bucket列表 static getBucketList() async { - String method = 'GET'; - String canonicalizedResource = '/'; - String contentMd5 = ''; - String contentType = ''; - String host = 'oss-cn-hangzhou.aliyuncs.com'; - String authorization = await aliyunAuthorization(method, canonicalizedResource, {}, contentMd5, contentType); - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': authorization, - 'Date': HttpDate.format(DateTime.now()), - }; - Map queryParameters = { - 'max-keys': 1000, - }; - - Dio dio = Dio(baseoptions); try { + String method = 'GET'; + String canonicalizedResource = '/'; + String contentMd5 = ''; + String contentType = ''; + String host = 'oss-cn-hangzhou.aliyuncs.com'; + String authorization = await aliyunAuthorization(method, canonicalizedResource, {}, contentMd5, contentType); + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': authorization, + 'Date': HttpDate.format(DateTime.now()), + }; + Map queryParameters = { + 'max-keys': 1000, + }; + + Dio dio = Dio(baseoptions); + var response = await dio.get('https://$host', queryParameters: queryParameters); if (response.statusCode == 200) { String responseBody = response.data; @@ -177,65 +182,54 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "AliyunManageAPI", "getBucketList"); return [e.toString()]; } } //新建存储桶 static createBucket(Map newBucketConfigMap) async { - String method = 'PUT'; - String bucketName = newBucketConfigMap['bucketName']; - String region = newBucketConfigMap['region']; - bool multiAZ = newBucketConfigMap['multiAZ']; - String xCosACL = newBucketConfigMap['xCosACL']; - - if (multiAZ == true && - region != 'oss-cn-shenzhen' && - region != 'oss-cn-beijing' && - region != 'oss-cn-hangzhou' && - region != 'oss-cn-shanghai' && - region != 'oss-cn-hongkong' && - region != 'oss-ap-southeast-1' && - region != 'oss-ap-southeast-5') { - return [ - 'multiAZ error', - ]; - } - var body = 'ZRS'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - 'x-oss-acl': xCosACL, - }; - if (multiAZ == true) { - baseoptions.headers['content-type'] = 'application/xml'; - baseoptions.headers['content-length'] = body.length.toString(); - String contentMd5 = await getContentMd5(body); - baseoptions.headers['content-md5'] = await getContentMd5(body); - String authorization = - await aliyunAuthorization(method, '/$bucketName/', baseoptions.headers, contentMd5, 'application/xml'); - baseoptions.headers['Authorization'] = authorization; - } else { - baseoptions.headers['content-type'] = 'application/json'; - String authorization = - await aliyunAuthorization(method, '/$bucketName/', baseoptions.headers, '', 'application/json'); - baseoptions.headers['Authorization'] = authorization; - } - Dio dio = Dio(baseoptions); try { + String method = 'PUT'; + String bucketName = newBucketConfigMap['bucketName']; + String region = newBucketConfigMap['region']; + bool multiAZ = newBucketConfigMap['multiAZ']; + String xCosACL = newBucketConfigMap['xCosACL']; + + if (multiAZ == true && + region != 'oss-cn-shenzhen' && + region != 'oss-cn-beijing' && + region != 'oss-cn-hangzhou' && + region != 'oss-cn-shanghai' && + region != 'oss-cn-hongkong' && + region != 'oss-ap-southeast-1' && + region != 'oss-ap-southeast-5') { + return [ + 'multiAZ error', + ]; + } + var body = 'ZRS'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + 'x-oss-acl': xCosACL, + }; + if (multiAZ == true) { + baseoptions.headers['content-type'] = 'application/xml'; + baseoptions.headers['content-length'] = body.length.toString(); + String contentMd5 = await getContentMd5(body); + baseoptions.headers['content-md5'] = await getContentMd5(body); + String authorization = + await aliyunAuthorization(method, '/$bucketName/', baseoptions.headers, contentMd5, 'application/xml'); + baseoptions.headers['Authorization'] = authorization; + } else { + baseoptions.headers['content-type'] = 'application/json'; + String authorization = + await aliyunAuthorization(method, '/$bucketName/', baseoptions.headers, '', 'application/json'); + baseoptions.headers['Authorization'] = authorization; + } + Dio dio = Dio(baseoptions); + Response response; if (multiAZ == true) { response = await dio.put('https://$bucketName.$region.aliyuncs.com', data: body); @@ -248,43 +242,33 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "createBucket", - text: formatErrorMessage({ - 'newBucketConfigMap': newBucketConfigMap, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "createBucket", - text: formatErrorMessage({ - 'newBucketConfigMap': newBucketConfigMap, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'newBucketConfigMap': newBucketConfigMap, + }, + "AliyunManageAPI", + "createBucket"); return [e.toString()]; } } //查询存储桶权限 static queryACLPolicy(Map element) async { - String bucket = element['name']; - String region = element['location']; - String method = 'GET'; - String urlpath = '/$bucket/?acl'; - String host = '$bucket.$region.aliyuncs.com'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - }; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', ''); - baseoptions.headers['Authorization'] = authorization; - try { + String bucket = element['name']; + String region = element['location']; + String method = 'GET'; + String urlpath = '/$bucket/?acl'; + String host = '$bucket.$region.aliyuncs.com'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + }; + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', ''); + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); var response = await dio.get('https://$host?acl', queryParameters: {'acl': ''}); var responseBody = response.data; @@ -297,46 +281,31 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "queryACLPolicy", - text: formatErrorMessage({ - 'element': element, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "queryACLPolicy", - text: formatErrorMessage({ - 'element': element, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "AliyunManageAPI", "queryACLPolicy"); return [e.toString()]; } } //删除存储桶 static deleteBucket(Map element) async { - String bucket = element['name']; - String region = element['location']; - - String method = 'DELETE'; - String urlpath = '/$bucket/'; - - String host = '$bucket.$region.aliyuncs.com'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - 'content-type': 'application/json', - }; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', 'application/json'); - - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); try { + String bucket = element['name']; + String region = element['location']; + + String method = 'DELETE'; + String urlpath = '/$bucket/'; + + String host = '$bucket.$region.aliyuncs.com'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + 'content-type': 'application/json', + }; + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', 'application/json'); + + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + var response = await dio.delete('https://$host'); if (response.statusCode == 204) { @@ -345,49 +314,39 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({ - 'element': element, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({ - 'element': element, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + }, + "AliyunManageAPI", + "deleteBucket"); return [e.toString()]; } } //更改存储桶权限 static changeACLPolicy(Map element, String newACL) async { - String bucket = element['name']; - String region = element['location']; + try { + String bucket = element['name']; + String region = element['location']; - String method = 'PUT'; - String urlpath = '/$bucket/?acl'; + String method = 'PUT'; + String urlpath = '/$bucket/?acl'; - String host = '$bucket.$region.aliyuncs.com'; - BaseOptions baseoptions = setBaseOptions(); - Map header = { - 'Date': HttpDate.format(DateTime.now()), - 'x-oss-acl': newACL, - 'content-type': 'application/json', - }; - String authorization = await aliyunAuthorization(method, urlpath, header, '', 'application/json'); + String host = '$bucket.$region.aliyuncs.com'; + BaseOptions baseoptions = setBaseOptions(); + Map header = { + 'Date': HttpDate.format(DateTime.now()), + 'x-oss-acl': newACL, + 'content-type': 'application/json', + }; + String authorization = await aliyunAuthorization(method, urlpath, header, '', 'application/json'); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - try { var response = await dio.put('https://$host/?acl'); if (response.statusCode == 200) { return ['success']; @@ -395,25 +354,14 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "changeACLPolicy", - text: formatErrorMessage({ - 'element': element, - 'newACL': newACL, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "changeACLPolicy", - text: formatErrorMessage({ - 'element': element, - 'newACL': newACL, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'newACL': newACL, + }, + "AliyunManageAPI", + "changeACLPolicy"); return [e.toString()]; } } @@ -456,25 +404,25 @@ class AliyunManageAPI { //查询存储桶文件列表 static queryBucketFiles(Map element, Map query) async { - String bucket = element['name']; - String region = element['location']; - - String method = 'GET'; - String urlpath = '/$bucket/'; - - String host = '$bucket.$region.aliyuncs.com'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - }; - query['max-keys'] = 1000; - query['list-type'] = 2; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', ''); - baseoptions.headers['Authorization'] = authorization; + try { + String bucket = element['name']; + String region = element['location']; + + String method = 'GET'; + String urlpath = '/$bucket/'; + + String host = '$bucket.$region.aliyuncs.com'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + }; + query['max-keys'] = 1000; + query['list-type'] = 2; + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', ''); + baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); + Dio dio = Dio(baseoptions); - try { String marker = ''; var response = await dio.get('https://$host/', queryParameters: query); var responseBody = response.data; @@ -542,25 +490,14 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({ - 'element': element, - 'query': query, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({ - 'element': element, - 'query': query, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'query': query, + }, + "AliyunManageAPI", + "queryBucketFiles"); return [e.toString()]; } } @@ -582,32 +519,33 @@ class AliyunManageAPI { //重命名文件 static copyFile(Map element, String key, String newKey) async { - String bucket = element['name']; - String region = element['location']; - String method = 'PUT'; - String host = '$bucket.$region.aliyuncs.com'; - String newName = ''; - if (key.substring(0, key.lastIndexOf('/') + 1) == '') { - newName = newKey; - } else { - newName = key.substring(0, key.lastIndexOf('/') + 1) + newKey; - } - - String urlpath = '/$bucket/$newName'; - String xOssCopySource = '/$bucket/${Uri.encodeComponent(key)}'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - 'x-oss-copy-source': xOssCopySource, - 'x-oss-forbid-overwrite': 'false', - 'Host': host, - 'content-type': 'application/json', - }; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', 'application/json'); - baseoptions.headers['Authorization'] = authorization; - - Dio dio = Dio(baseoptions); try { + String bucket = element['name']; + String region = element['location']; + String method = 'PUT'; + String host = '$bucket.$region.aliyuncs.com'; + String newName = ''; + if (key.substring(0, key.lastIndexOf('/') + 1) == '') { + newName = newKey; + } else { + newName = key.substring(0, key.lastIndexOf('/') + 1) + newKey; + } + + String urlpath = '/$bucket/$newName'; + String xOssCopySource = '/$bucket/${Uri.encodeComponent(key)}'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + 'x-oss-copy-source': xOssCopySource, + 'x-oss-forbid-overwrite': 'false', + 'Host': host, + 'content-type': 'application/json', + }; + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, '', 'application/json'); + baseoptions.headers['Authorization'] = authorization; + + Dio dio = Dio(baseoptions); + var response = await dio.put('https://$host/${Uri.encodeComponent(newName)}'); if (response.statusCode == 200) { return ['success']; @@ -615,53 +553,41 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "copyFile", - text: formatErrorMessage({ - 'element': element, - 'key': key, - 'newKey': newKey, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "copyFile", - text: formatErrorMessage({ - 'element': element, - 'key': key, - 'newKey': newKey, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'key': key, + 'newKey': newKey, + }, + "AliyunManageAPI", + "copyFile"); return [e.toString()]; } } //删除文件 static deleteFile(Map element, String key) async { - String bucket = element['name']; - String region = element['location']; - String method = 'DELETE'; - String urlpath = '/$bucket/$key'; - String host = '$bucket.$region.aliyuncs.com'; + try { + String bucket = element['name']; + String region = element['location']; + String method = 'DELETE'; + String urlpath = '/$bucket/$key'; + String host = '$bucket.$region.aliyuncs.com'; - BaseOptions baseoptions = setBaseOptions(); - String contentMD5 = ''; - String contentType = 'application/json'; - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - 'content-type': contentType, - }; + BaseOptions baseoptions = setBaseOptions(); + String contentMD5 = ''; + String contentType = 'application/json'; + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + 'content-type': contentType, + }; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, contentMD5, contentType); + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, contentMD5, contentType); - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - try { var response = await dio.delete('https://$host/$key'); if (response.statusCode == HttpStatus.noContent) { return ['success']; @@ -669,25 +595,14 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({ - 'element': element, - 'key': key, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({ - 'element': element, - 'key': key, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'key': key, + }, + "AliyunManageAPI", + "deleteFile"); return [e.toString()]; } } @@ -781,31 +696,32 @@ class AliyunManageAPI { //新建文件夹 static createFolder(Map element, String prefix, String newfolder) async { - String bucket = element['name']; - String region = element['location']; + try { + String bucket = element['name']; + String region = element['location']; - String method = 'PUT'; + String method = 'PUT'; - String host = '$bucket.$region.aliyuncs.com'; + String host = '$bucket.$region.aliyuncs.com'; + + String urlpath = '/$bucket/$prefix$newfolder/'; + if (urlpath.substring(urlpath.length - 1) != '/') { + urlpath = '$urlpath/'; + } + BaseOptions baseoptions = setBaseOptions(); + String contentMD5 = ''; + String contentType = 'application/json'; + baseoptions.headers = { + 'Date': HttpDate.format(DateTime.now()), + 'content-type': contentType, + 'content-length': '0', + 'Host': host, + }; + String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, contentMD5, contentType); + + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - String urlpath = '/$bucket/$prefix$newfolder/'; - if (urlpath.substring(urlpath.length - 1) != '/') { - urlpath = '$urlpath/'; - } - BaseOptions baseoptions = setBaseOptions(); - String contentMD5 = ''; - String contentType = 'application/json'; - baseoptions.headers = { - 'Date': HttpDate.format(DateTime.now()), - 'content-type': contentType, - 'content-length': '0', - 'Host': host, - }; - String authorization = await aliyunAuthorization(method, urlpath, baseoptions.headers, contentMD5, contentType); - - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { String url = 'https://$host/$prefix$newfolder'; if (url.substring(url.length - 1) != '/') { url = '$url/'; @@ -817,27 +733,15 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "createFolder", - text: formatErrorMessage({ - 'element': element, - 'prefix': prefix, - 'newfolder': newfolder, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "createFolder", - text: formatErrorMessage({ - 'element': element, - 'prefix': prefix, - 'newfolder': newfolder, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'prefix': prefix, + 'newfolder': newfolder, + }, + "AliyunManageAPI", + "createFolder"); return [e.toString()]; } } @@ -849,51 +753,52 @@ class AliyunManageAPI { String filepath, String prefix, ) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String keyId = configMap['keyId']; - String keySecret = configMap['keySecret']; - String host = '$bucket.$region.aliyuncs.com'; - //不要加/,否则会导致签名错误 - String urlpath = '$prefix$filename'; - //上传策略 - Map uploadPolicy = { - "expiration": "2034-12-01T12:00:00.000Z", - "conditions": [ - {"bucket": bucket}, - ["content-length-range", 0, 104857600], - {"key": urlpath} - ] - }; - String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); - String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); - - Map formMap = { - 'key': urlpath, - 'OSSAccessKeyId': keyId, - 'policy': base64Policy, - 'Signature': singature, - 'file': await MultipartFile.fromFile(filepath, filename: filename), - }; - if (getContentType(my_path.extension(filepath)) != null) { - formMap['x-oss-content-type'] = getContentType(my_path.extension(filepath)); - } - FormData formData = FormData.fromMap(formMap); - - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(filepath); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String keyId = configMap['keyId']; + String keySecret = configMap['keySecret']; + String host = '$bucket.$region.aliyuncs.com'; + //不要加/,否则会导致签名错误 + String urlpath = '$prefix$filename'; + //上传策略 + Map uploadPolicy = { + "expiration": "2034-12-01T12:00:00.000Z", + "conditions": [ + {"bucket": bucket}, + ["content-length-range", 0, 104857600], + {"key": urlpath} + ] + }; + String base64Policy = base64.encode(utf8.encode(json.encode(uploadPolicy))); + String singature = base64.encode(Hmac(sha1, utf8.encode(keySecret)).convert(utf8.encode(base64Policy)).bytes); + + Map formMap = { + 'key': urlpath, + 'OSSAccessKeyId': keyId, + 'policy': base64Policy, + 'Signature': singature, + 'file': await MultipartFile.fromFile(filepath, filename: filename), + }; + if (getContentType(my_path.extension(filepath)) != null) { + formMap['x-oss-content-type'] = getContentType(my_path.extension(filepath)); + } + FormData formData = FormData.fromMap(formMap); + + BaseOptions baseoptions = setBaseOptions(); + File uploadFile = File(filepath); + String contentLength = await uploadFile.length().then((value) { + return value.toString(); + }); + baseoptions.headers = { + 'Host': host, + 'Content-Type': Global.multipartString, + 'Content-Length': contentLength, + }; + Dio dio = Dio(baseoptions); + var response = await dio.post( 'https://$host', data: formData, @@ -904,29 +809,16 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({ - 'element': element, - 'filename': filename, - 'filepath': filepath, - 'prefix': prefix, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({ - 'element': element, - 'filename': filename, - 'filepath': filepath, - 'prefix': prefix, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'element': element, + 'filename': filename, + 'filepath': filepath, + 'prefix': prefix, + }, + "AliyunManageAPI", + "uploadFile"); return ['error']; } } @@ -995,20 +887,8 @@ class AliyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AliyunManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AliyunManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, {'fileLink': fileLink, 'element': element, 'prefix': prefix}, "AliyunManageAPI", "uploadNetworkFile"); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/aws_manage_api.dart b/lib/picture_host_manage/manage_api/aws_manage_api.dart index 726679f..0490f91 100644 --- a/lib/picture_host_manage/manage_api/aws_manage_api.dart +++ b/lib/picture_host_manage/manage_api/aws_manage_api.dart @@ -18,7 +18,7 @@ class AwsManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_aws_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_aws_config.txt')); } static Future get _localPath async { @@ -43,6 +43,9 @@ class AwsManageAPI { static Future getConfigMap() async { String configStr = await readAwsConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -85,28 +88,29 @@ class AwsManageAPI { //获取存储桶列表 static getBucketList() async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String endpoint = configMap['endpoint']; - String region = configMap['region']; - - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String endpoint = configMap['endpoint']; + String region = configMap['region']; + + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); + } + var response = await minio.listBuckets(); return ['success', response]; } catch (e) { @@ -121,23 +125,23 @@ class AwsManageAPI { } static getBucketRegion(String bucket) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String endpoint = configMap['endpoint']; + try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String endpoint = configMap['endpoint']; - if (endpoint.contains('amazonaws.com')) { - endpoint = 's3.us-east-1.amazonaws.com'; - } - Minio minio; + if (endpoint.contains('amazonaws.com')) { + endpoint = 's3.us-east-1.amazonaws.com'; + } + Minio minio; - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); - try { var response = await minio.getBucketRegion(bucket); return ['success', response]; @@ -154,35 +158,36 @@ class AwsManageAPI { //新建存储桶 static createBucket(Map newBucketConfigMap) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = newBucketConfigMap['bucketName']; - String endpoint = configMap['endpoint']; - String region = newBucketConfigMap['region']; - - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; + try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = newBucketConfigMap['bucketName']; + String endpoint = configMap['endpoint']; + String region = newBucketConfigMap['region']; + + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } - try { if (region == 'None') { await minio.makeBucket(bucket); } else { @@ -201,34 +206,35 @@ class AwsManageAPI { //删除存储桶 static deleteBucket(Map element) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; - String region = element['region']; - - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; - } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; + String region = element['region']; + + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); + } + var response = await minio.removeBucket(bucket); return ['success', response]; @@ -292,36 +298,37 @@ class AwsManageAPI { //查询存储桶文件列表 static queryBucketFiles(Map element, Map query) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; + try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; - String region = element['region']; + String region = element['region']; - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } - try { Map objects = { 'contents': [], 'commonPrefixes': [], @@ -347,35 +354,36 @@ class AwsManageAPI { //删除文件 static deleteFile(Map element, String key) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; - String region = element['region']; - - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; + try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; + String region = element['region']; + + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } - try { var response = await minio.removeObject(bucket, key); return ['success', response]; @@ -392,10 +400,11 @@ class AwsManageAPI { //删除文件夹 static deleteFolder(Map element, String prefix) async { - var queryResult = await queryBucketFiles(element, { - 'prefix': prefix, - }); try { + var queryResult = await queryBucketFiles(element, { + 'prefix': prefix, + }); + if (queryResult[0] == 'success') { List files = []; List folders = []; @@ -435,33 +444,34 @@ class AwsManageAPI { //重命名文件 static copyFile(Map element, String key, String newKey) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; - String region = element['region']; - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; - } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; + String region = element['region']; + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); + } + var response = await minio.copyObject( bucket, newKey, @@ -523,33 +533,34 @@ class AwsManageAPI { //新建文件夹 static createFolder(Map element, String prefix, String newfolder) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; - String region = element['region']; - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; - } - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; + String region = element['region']; + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); + } + var response = await minio.putObject(bucket, '$prefix$newfolder/placeholder.txt', Stream.fromIterable([])); return ['success', response]; } catch (e) { @@ -570,42 +581,43 @@ class AwsManageAPI { String filepath, String prefix, ) async { - Map configMap = await getConfigMap(); - String accessKeyId = configMap['accessKeyId']; - String secretAccessKey = configMap['secretAccessKey']; - String bucket = element['name']; - String endpoint = configMap['endpoint']; - String region = element['region']; - String uploadPath = prefix; - if (endpoint.contains('amazonaws.com')) { - if (!endpoint.contains(region)) { - endpoint = 's3.$region.amazonaws.com'; + try { + Map configMap = await getConfigMap(); + String accessKeyId = configMap['accessKeyId']; + String secretAccessKey = configMap['secretAccessKey']; + String bucket = element['name']; + String endpoint = configMap['endpoint']; + String region = element['region']; + String uploadPath = prefix; + if (endpoint.contains('amazonaws.com')) { + if (!endpoint.contains(region)) { + endpoint = 's3.$region.amazonaws.com'; + } + } + //云存储的路径 + String urlpath = ''; + if (uploadPath != '') { + urlpath = '$uploadPath$filename'; + } else { + urlpath = filename; + } + + Minio minio; + if (region == 'None') { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + ); + } else { + minio = Minio( + endPoint: endpoint, + accessKey: accessKeyId, + secretKey: secretAccessKey, + region: region, + ); } - } - //云存储的路径 - String urlpath = ''; - if (uploadPath != '') { - urlpath = '$uploadPath$filename'; - } else { - urlpath = filename; - } - Minio minio; - if (region == 'None') { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - ); - } else { - minio = Minio( - endPoint: endpoint, - accessKey: accessKeyId, - secretKey: secretAccessKey, - region: region, - ); - } - try { Stream stream = File(filepath).openRead().cast(); await minio.putObject( bucket, @@ -689,20 +701,7 @@ class AwsManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "AwsManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "AwsManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'fileLink': fileLink, 'element': element, 'prefix': prefix}, "AwsManageAPI", "uploadNetworkFile"); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/ftp_manage_api.dart b/lib/picture_host_manage/manage_api/ftp_manage_api.dart index 0833064..438ed93 100644 --- a/lib/picture_host_manage/manage_api/ftp_manage_api.dart +++ b/lib/picture_host_manage/manage_api/ftp_manage_api.dart @@ -16,7 +16,7 @@ class FTPManageAPI { static Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_ftp_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_ftp_config.txt')); } static Future get _localPath async { @@ -41,6 +41,9 @@ class FTPManageAPI { static Future getConfigMap() async { String configStr = await readFTPConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -54,12 +57,13 @@ class FTPManageAPI { } static getDirectoryContentSFTP(String folder) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -103,12 +107,13 @@ class FTPManageAPI { } static executeCommandSFTP(String command) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -131,12 +136,13 @@ class FTPManageAPI { } static renameFileSFTP(String oldName, String newName) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -161,12 +167,13 @@ class FTPManageAPI { //新建文件夹 static createFolderSFTP(String folderName) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -190,12 +197,13 @@ class FTPManageAPI { //删除文件夹 static deleteFolderSFTP(String folderName) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -205,10 +213,7 @@ class FTPManageAPI { }, ); //no * in folderName - if (folderName.contains('*')) { - return ["failed"]; - } - if (folderName.contains('?')) { + if (folderName.contains('*') || folderName.contains('?') || folderName == '/') { return ["failed"]; } @@ -227,12 +232,13 @@ class FTPManageAPI { //删除文件 static deleteFileSFTP(String fileName) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -241,12 +247,10 @@ class FTPManageAPI { return ftpPassword; }, ); - if (fileName.contains('*')) { - return ["failed"]; - } - if (fileName.contains('?')) { + if (fileName.contains('*') || fileName == '/' || fileName.contains('?')) { return ["failed"]; } + await client.run('rm -f $fileName'); client.close(); return ['success', '']; @@ -292,12 +296,13 @@ class FTPManageAPI { } static uploadFileSFTP(String uploadPath, String filePath, String fileName) async { - Map configMap = await getConfigMap(); - String ftpHost = configMap['ftpHost']; - String ftpPort = configMap['ftpPort']; - String ftpUser = configMap['ftpUser']; - String ftpPassword = configMap['ftpPassword']; try { + Map configMap = await getConfigMap(); + String ftpHost = configMap['ftpHost']; + String ftpPort = configMap['ftpPort']; + String ftpUser = configMap['ftpUser']; + String ftpPassword = configMap['ftpPassword']; + final socket = await SSHSocket.connect(ftpHost, int.parse(ftpPort)); final client = SSHClient( socket, @@ -366,23 +371,13 @@ class FTPManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "FTPManageAPI", - methodName: "uploadNetworkFileSFTP", - text: formatErrorMessage({ - 'fileLink': fileLink, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "FTPManageAPI", - methodName: "uploadNetworkFileSFTP", - text: formatErrorMessage({ - 'fileLink': fileLink, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'fileLink': fileLink, + }, + "FTPManageAPI", + "uploadNetworkFileSFTP"); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/github_manage_api.dart b/lib/picture_host_manage/manage_api/github_manage_api.dart index 8c0c56a..a037227 100644 --- a/lib/picture_host_manage/manage_api/github_manage_api.dart +++ b/lib/picture_host_manage/manage_api/github_manage_api.dart @@ -15,7 +15,7 @@ class GithubManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_github_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_github_config.txt')); } static Future get _localPath async { @@ -40,6 +40,9 @@ class GithubManageAPI { static Future getConfigMap() async { String configStr = await readGithubConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -53,19 +56,20 @@ class GithubManageAPI { } static getUserInfo() async { - Map configMap = await getConfigMap(); - String githubusername = configMap['githubusername']; - String token = configMap['token']; - String host = 'https://api.github.com/users/$githubusername'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; - - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String githubusername = configMap['githubusername']; + String token = configMap['token']; + String host = 'https://api.github.com/users/$githubusername'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); + var response = await dio.get(host); if (response.statusCode == 200) { return ['success', response.data]; @@ -73,40 +77,29 @@ class GithubManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "getUserInfo", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "getUserInfo", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "GithubManageAPI", "getUserInfo"); return [e.toString()]; } } // 获取仓库列表 static getReposList() async { - List reposList = []; - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/user/repos'; - int page = 1; - int perPage = 10; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; - - Dio dio = Dio(baseoptions); try { + List reposList = []; + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/user/repos'; + int page = 1; + int perPage = 10; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); + var response = await dio.get(host, queryParameters: {'page': page, 'per_page': perPage}); if (response.statusCode == 200) { if (response.data.length > 0) { @@ -131,41 +124,30 @@ class GithubManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "getReposList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "getReposList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "GithubManageAPI", "getReposList"); return [e.toString()]; } } // 获取仓库列表 static getOtherReposList(String username) async { - List reposList = []; - Map configMap = await getConfigMap(); - String token = configMap['token']; + try { + List reposList = []; + Map configMap = await getConfigMap(); + String token = configMap['token']; - String host = 'https://api.github.com/users/$username/repos'; - int page = 1; - int perPage = 10; + String host = 'https://api.github.com/users/$username/repos'; + int page = 1; + int perPage = 10; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.get(host, queryParameters: {'page': page, 'per_page': perPage}); if (response.statusCode == 200) { if (response.data.length > 0) { @@ -190,37 +172,26 @@ class GithubManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "getOtherReposList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "getOtherReposList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "GithubManageAPI", "getOtherReposList"); return [e.toString()]; } } //创建仓库 static createRepo(Map newRepoInfo) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/user/repos'; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/user/repos'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.post(host, data: newRepoInfo); if (response.statusCode == 201) { return showToast('创建成功'); @@ -228,41 +199,32 @@ class GithubManageAPI { return showToast('创建失败'); } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "createRepo", - text: formatErrorMessage({ - 'newRepoInfo': newRepoInfo, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "createRepo", - text: formatErrorMessage({ - 'newRepoInfo': newRepoInfo, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'newRepoInfo': newRepoInfo, + }, + "GithubManageAPI", + "createRepo"); return showToast('创建失败'); } } //获取仓库根目录sha static getRootDirSha(String username, String repoName, String branch) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/repos/$username/$repoName/branches/$branch'; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/repos/$username/$repoName/branches/$branch'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.get(host); if (response.statusCode == 200) { String sha = response.data['commit']['commit']['tree']['sha']; @@ -272,43 +234,33 @@ class GithubManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "getRootDirSha", - text: formatErrorMessage({ - 'username': username, - 'repoName': repoName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "getRootDirSha", - text: formatErrorMessage({ - 'username': username, - 'repoName': repoName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'username': username, + 'repoName': repoName, + }, + "GithubManageAPI", + "getRootDirSha"); return [e.toString()]; } } //获取仓库目录文件列表 static getRepoDirList(String username, String repoName, String sha) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/repos/$username/$repoName/git/trees/$sha'; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/repos/$username/$repoName/git/trees/$sha'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.get(host); if (response.statusCode == 200) { List fileList = response.data['tree']; @@ -317,43 +269,33 @@ class GithubManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "GithubManageAPI", - methodName: "getRepoDirList", - text: formatErrorMessage({ - 'username': username, - 'repoName': repoName, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "GithubManageAPI", - methodName: "getRepoDirList", - text: formatErrorMessage({ - 'username': username, - 'repoName': repoName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'username': username, + 'repoName': repoName, + }, + "GithubManageAPI", + "getRepoDirList"); return [e.toString()]; } } //判断是否是空目录 static isDirEmpty(String username, String repoName, String bucketPrefix) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/repos/$username/$repoName/contents/$bucketPrefix'; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/repos/$username/$repoName/contents/$bucketPrefix'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.get(host); if (response.statusCode == 200) { List fileList = response.data; @@ -393,18 +335,19 @@ class GithubManageAPI { //获取仓库文件内容 static getRepoFileContent(String username, String repoName, String filePath) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/repos/$username/$repoName/contents/$filePath'; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/repos/$username/$repoName/contents/$filePath'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Dio dio = Dio(baseoptions); - Dio dio = Dio(baseoptions); - try { var response = await dio.get(host); if (response.statusCode == 200) { var content = response.data; @@ -438,24 +381,25 @@ class GithubManageAPI { //删除仓库文件 static deleteRepoFile(String username, String repoName, String path, String sha, String branch) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String host = 'https://api.github.com/repos/$username/$repoName/contents/$path'; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; - - Map data = { - 'message': 'deleted by PicHoro app', - 'sha': sha, - 'branch': branch, - }; - - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String host = 'https://api.github.com/repos/$username/$repoName/contents/$path'; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + Map data = { + 'message': 'deleted by PicHoro app', + 'sha': sha, + 'branch': branch, + }; + + Dio dio = Dio(baseoptions); + var response = await dio.delete(host, data: data); if (response.statusCode == 200) { return ['success']; @@ -581,49 +525,50 @@ class GithubManageAPI { //新建文件夹 static createFolder(Map element, String newPrefix) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - - String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; - String appDir = await getApplicationDocumentsDirectory().then((value) { - return value.path; - }); - String assetFilePath = '$appDir/PicHoroValidate.jpeg'; - File assetFile = File(assetFilePath); - - if (!assetFile.existsSync()) { - ByteData data = await rootBundle.load(assetPath); - List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await assetFile.writeAsBytes(bytes); - } - String base64Image = base64Encode(File(assetFilePath).readAsBytesSync()); + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; - Map queryBody = { - 'message': 'uploaded by PicHoro app', - 'content': base64Image, - 'branch': element['default_branch'], - }; + String assetPath = 'assets/validateImage/PicHoroValidate.jpeg'; + String appDir = await getApplicationDocumentsDirectory().then((value) { + return value.path; + }); + String assetFilePath = '$appDir/PicHoroValidate.jpeg'; + File assetFile = File(assetFilePath); - BaseOptions baseoptions = setBaseOptions(); + if (!assetFile.existsSync()) { + ByteData data = await rootBundle.load(assetPath); + List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + await assetFile.writeAsBytes(bytes); + } + String base64Image = base64Encode(File(assetFilePath).readAsBytesSync()); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + Map queryBody = { + 'message': 'uploaded by PicHoro app', + 'content': base64Image, + 'branch': element['default_branch'], + }; - String trimedPath = newPrefix.toString().trim(); + BaseOptions baseoptions = setBaseOptions(); + + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; + + String trimedPath = newPrefix.toString().trim(); + + if (trimedPath.startsWith('/')) { + trimedPath = trimedPath.substring(1); + } + if (trimedPath.endsWith('/')) { + trimedPath = trimedPath.substring(0, trimedPath.length - 1); + } + Dio dio = Dio(baseoptions); + String uploadUrl = ''; + uploadUrl = + "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$trimedPath/PicHoroValidate.jpeg"; - if (trimedPath.startsWith('/')) { - trimedPath = trimedPath.substring(1); - } - if (trimedPath.endsWith('/')) { - trimedPath = trimedPath.substring(0, trimedPath.length - 1); - } - Dio dio = Dio(baseoptions); - String uploadUrl = ''; - uploadUrl = - "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$trimedPath/PicHoroValidate.jpeg"; - try { var response = await dio.put(uploadUrl, data: queryBody); if (response.statusCode == 200 || response.statusCode == 201) { return [ @@ -656,41 +601,41 @@ class GithubManageAPI { //上传文件 static uploadFile(Map element, String filename, String filePath, String newPrefix) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - String base64Image = base64Encode(File(filePath).readAsBytesSync()); + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + String base64Image = base64Encode(File(filePath).readAsBytesSync()); - Map queryBody = { - 'message': 'uploaded by PicHoro app', - 'content': base64Image, - 'branch': element['default_branch'], - }; + Map queryBody = { + 'message': 'uploaded by PicHoro app', + 'content': base64Image, + 'branch': element['default_branch'], + }; - BaseOptions baseoptions = setBaseOptions(); + BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Accept': 'application/vnd.github+json', - }; + baseoptions.headers = { + 'Authorization': token, + 'Accept': 'application/vnd.github+json', + }; - String trimedPath = newPrefix.toString().trim(); + String trimedPath = newPrefix.toString().trim(); - if (trimedPath.startsWith('/')) { - trimedPath = trimedPath.substring(1); - } - if (trimedPath.endsWith('/')) { - trimedPath = trimedPath.substring(0, trimedPath.length - 1); - } - Dio dio = Dio(baseoptions); - String uploadUrl = ''; - if (trimedPath == '') { - uploadUrl = "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$filename"; - } else { - uploadUrl = - "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$trimedPath/$filename"; - } + if (trimedPath.startsWith('/')) { + trimedPath = trimedPath.substring(1); + } + if (trimedPath.endsWith('/')) { + trimedPath = trimedPath.substring(0, trimedPath.length - 1); + } + Dio dio = Dio(baseoptions); + String uploadUrl = ''; + if (trimedPath == '') { + uploadUrl = "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$filename"; + } else { + uploadUrl = + "https://api.github.com/repos/${configMap["githubusername"]}/${element["name"]}/contents/$trimedPath/$filename"; + } - try { var response = await dio.put(uploadUrl, data: queryBody); if (response.statusCode == 200 || response.statusCode == 201) { return [ diff --git a/lib/picture_host_manage/manage_api/imgur_manage_api.dart b/lib/picture_host_manage/manage_api/imgur_manage_api.dart index da1cfc4..026da99 100644 --- a/lib/picture_host_manage/manage_api/imgur_manage_api.dart +++ b/lib/picture_host_manage/manage_api/imgur_manage_api.dart @@ -14,7 +14,7 @@ class ImgurManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_imgur_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_imgur_config.txt')); } static Future get _localPath async { @@ -75,6 +75,9 @@ class ImgurManageAPI { static Future getConfigMap() async { String configStr = await readImgurConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } diff --git a/lib/picture_host_manage/manage_api/lskypro_manage_api.dart b/lib/picture_host_manage/manage_api/lskypro_manage_api.dart index 8d57437..66c54d8 100644 --- a/lib/picture_host_manage/manage_api/lskypro_manage_api.dart +++ b/lib/picture_host_manage/manage_api/lskypro_manage_api.dart @@ -13,7 +13,7 @@ class LskyproManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_host_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_host_config.txt')); } static Future get _localPath async { @@ -38,6 +38,9 @@ class LskyproManageAPI { static Future getConfigMap() async { String configStr = await readLskyproConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -51,17 +54,18 @@ class LskyproManageAPI { } static getUserInfo() async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - }; - Dio dio = Dio(options); - String userInfoUrl = "$host/api/v1/profile"; try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + }; + Dio dio = Dio(options); + String userInfoUrl = "$host/api/v1/profile"; + var response = await dio.get(userInfoUrl); if (response.statusCode == 200 && response.data!['status'] == true) { return ['success', response.data]; @@ -87,17 +91,18 @@ class LskyproManageAPI { } static getAlbums() async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - }; - Dio dio = Dio(options); - String userInfoUrl = "$host/api/v1/albums"; try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + }; + Dio dio = Dio(options); + String userInfoUrl = "$host/api/v1/albums"; + int page = 1; int lastPage = 1; List albums = []; @@ -139,18 +144,18 @@ class LskyproManageAPI { } static getPhoto(int? albumId) async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - }; - Dio dio = Dio(options); - String userInfoUrl = "$host/api/v1/images"; - try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + }; + Dio dio = Dio(options); + String userInfoUrl = "$host/api/v1/images"; + int page = 1; int lastPage = 1; List images = []; @@ -195,20 +200,21 @@ class LskyproManageAPI { //删除图片 static deleteFile(String deleteKey) async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - Map formdata = { - "key": deleteKey, - }; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - }; - Dio dio = Dio(options); - String deleteUrl = "$host/api/v1/images/$deleteKey"; try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + Map formdata = { + "key": deleteKey, + }; + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + }; + Dio dio = Dio(options); + String deleteUrl = "$host/api/v1/images/$deleteKey"; + var response = await dio.delete(deleteUrl, data: formdata); if (response.statusCode == 200 && response.data!['status'] == true) { return ['success']; @@ -235,20 +241,21 @@ class LskyproManageAPI { //删除相册 static deleteAlbum(String id) async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - Map formdata = { - "id": id, - }; - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - }; - Dio dio = Dio(options); - String deleteUrl = "$host/api/v1/albums/$id"; try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + Map formdata = { + "id": id, + }; + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + }; + Dio dio = Dio(options); + String deleteUrl = "$host/api/v1/albums/$id"; + var response = await dio.delete(deleteUrl, data: formdata); if (response.statusCode == 200 && response.data!['status'] == true) { return ['success']; @@ -274,29 +281,30 @@ class LskyproManageAPI { } static uploadFile(String filename, String path) async { - Map configMap = await getConfigMap(); - String token = configMap["token"]; - String host = configMap["host"]; - FormData formdata = FormData.fromMap({ - "file": await MultipartFile.fromFile(path, filename: filename), - }); - if (configMap["strategy_id"] == "None") { - formdata = FormData.fromMap({}); - } else { - formdata = FormData.fromMap({ + try { + Map configMap = await getConfigMap(); + String token = configMap["token"]; + String host = configMap["host"]; + FormData formdata = FormData.fromMap({ "file": await MultipartFile.fromFile(path, filename: filename), - "strategy_id": configMap["strategy_id"], }); - } - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": token, - "Accept": "application/json", - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); - String uploadUrl = "$host/api/v1/upload"; - try { + if (configMap["strategy_id"] == "None") { + formdata = FormData.fromMap({}); + } else { + formdata = FormData.fromMap({ + "file": await MultipartFile.fromFile(path, filename: filename), + "strategy_id": configMap["strategy_id"], + }); + } + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": token, + "Accept": "application/json", + "Content-Type": "multipart/form-data", + }; + Dio dio = Dio(options); + String uploadUrl = "$host/api/v1/upload"; + var response = await dio.post(uploadUrl, data: formdata); if (response.statusCode == 200 && response.data!['status'] == true) { return ['success']; diff --git a/lib/picture_host_manage/manage_api/qiniu_manage_api.dart b/lib/picture_host_manage/manage_api/qiniu_manage_api.dart index fb475a3..30251d6 100644 --- a/lib/picture_host_manage/manage_api/qiniu_manage_api.dart +++ b/lib/picture_host_manage/manage_api/qiniu_manage_api.dart @@ -35,7 +35,7 @@ class QiniuManageAPI { static Future get localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_qiniu_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_qiniu_config.txt')); } static Future get _localPath async { @@ -95,6 +95,9 @@ class QiniuManageAPI { static Future getConfigMap() async { String configStr = await readQiniuConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -223,22 +226,24 @@ signingStr=signingStr+ //获取存储桶名字列表 static getBucketNameList() async { - Map configMap = await getConfigMap(); - String method = 'GET'; - String urlpath = '/buckets'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qbox.me'; - - String authorization = await qiniuAuthorization(method, urlpath, null, host, null, null, '', accessKey, secretKey); - authorization = 'Qiniu $authorization'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': authorization, - 'Host': host, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String method = 'GET'; + String urlpath = '/buckets'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qbox.me'; + + String authorization = + await qiniuAuthorization(method, urlpath, null, host, null, null, '', accessKey, secretKey); + authorization = 'Qiniu $authorization'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': authorization, + 'Host': host, + }; + Dio dio = Dio(baseoptions); + var response = await dio.get('https://$host/buckets'); if (response.statusCode == 200) { return ['success', response.data]; @@ -246,47 +251,36 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "QiniuManageAPI", "getBucketList"); return [e.toString()]; } } //新建存储桶 static createBucket(Map newBucketConfigMap) async { - Map configMap = await getConfigMap(); - String bucket = newBucketConfigMap['bucketName']; - String region = newBucketConfigMap['region']; - - String method = 'POST'; - String urlpath = '/mkbucketv3/$bucket/region/$region'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qbox.me'; - - String authorization = - await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': 'Qiniu $authorization', - 'Host': host, - 'Content-Type': 'application/json', - }; - - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = newBucketConfigMap['bucketName']; + String region = newBucketConfigMap['region']; + + String method = 'POST'; + String urlpath = '/mkbucketv3/$bucket/region/$region'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qbox.me'; + + String authorization = + await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': 'Qiniu $authorization', + 'Host': host, + 'Content-Type': 'application/json', + }; + + Dio dio = Dio(baseoptions); + var response = await dio.post( 'https://$host$urlpath', ); @@ -297,42 +291,30 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "createBucket", - text: formatErrorMessage({'newBucketConfigMap': newBucketConfigMap}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "createBucket", - text: formatErrorMessage({'newBucketConfigMap': newBucketConfigMap}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'newBucketConfigMap': newBucketConfigMap}, "QiniuManageAPI", "createBucket"); return [e.toString()]; } } static getBucketACL(Map element) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String method = 'POST'; - String urlpath = '/v2/bucketInfo?bucket=$bucket&fs=true'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qiniuapi.com'; - String authorization = - await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': 'Qiniu $authorization', - 'Host': host, - 'Content-Type': 'application/json', - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String method = 'POST'; + String urlpath = '/v2/bucketInfo?bucket=$bucket&fs=true'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qiniuapi.com'; + String authorization = + await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': 'Qiniu $authorization', + 'Host': host, + 'Content-Type': 'application/json', + }; + Dio dio = Dio(baseoptions); + var response = await dio.post('https://$host$urlpath'); if (response.statusCode == 200) { @@ -341,45 +323,34 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "getBucketACL", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "getBucketACL", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "QiniuManageAPI", "getBucketACL"); return [e.toString()]; } } //删除存储桶 static deleteBucket(Map element) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - - String method = 'POST'; - String urlpath = '/drop/$bucket'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qbox.me'; - - String authorization = - await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': 'Qiniu $authorization', - 'Host': host, - 'Content-Type': 'application/json', - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + + String method = 'POST'; + String urlpath = '/drop/$bucket'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qbox.me'; + + String authorization = + await qiniuAuthorization(method, urlpath, null, host, 'application/json', null, '', accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': 'Qiniu $authorization', + 'Host': host, + 'Content-Type': 'application/json', + }; + Dio dio = Dio(baseoptions); + var response = await dio.post('https://$host$urlpath'); if (response.statusCode == 200) { @@ -388,40 +359,29 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "QiniuManageAPI", "deleteBucket"); return [e.toString()]; } } //查询绑定域名 static queryDomains(Map element) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; - String method = 'GET'; - String urlpath = '/v2/domains?tbl=$bucket'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qbox.me'; + String method = 'GET'; + String urlpath = '/v2/domains?tbl=$bucket'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qbox.me'; - String authorization = await qiniuAuthorization( - method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + String authorization = await qiniuAuthorization( + method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); - try { var response = await dio.get( 'https://uc.qbox.me/v2/domains', queryParameters: { @@ -442,40 +402,29 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "queryDomains", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "queryDomains", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "QiniuManageAPI", "queryDomains"); return [e.toString()]; } } //设置权限 static setACL(Map element, String acl) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + + String method = 'POST'; + String urlpath = '/private?bucket=$bucket&private=$acl'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'uc.qbox.me'; - String method = 'POST'; - String urlpath = '/private?bucket=$bucket&private=$acl'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'uc.qbox.me'; + String authorization = await qiniuAuthorization( + method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); - String authorization = await qiniuAuthorization( - method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); - try { var response = await dio.post( 'https://uc.qbox.me/private', queryParameters: { @@ -497,19 +446,7 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "setACL", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "setACL", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "QiniuManageAPI", "setACL"); return [e.toString()]; } } @@ -572,34 +509,34 @@ signingStr=signingStr+ //查询存储桶文件列表 static queryBucketFiles(Map element, Map query) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - - String method = 'GET'; - String urlpath = '/list'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'rsf.qiniuapi.com'; - String queryStr = ''; - - if (query['delimiter'] == null && query['prefix'] == null) { - queryStr = 'bucket=$bucket&limit=1000'; - } else if (query['delimiter'] == null && query['prefix'] != null) { - queryStr = 'bucket=$bucket&limit=1000&prefix=${Uri.encodeComponent(query['prefix'])}'; - } else if (query['delimiter'] != null && query['prefix'] == null) { - queryStr = 'bucket=$bucket&limit=1000&delimiter=${Uri.encodeComponent(query['delimiter'])}'; - } else { - queryStr = - 'bucket=$bucket&limit=1000&prefix=${Uri.encodeComponent(query['prefix'])}&delimiter=${Uri.encodeComponent(query['delimiter'])}'; - } + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + + String method = 'GET'; + String urlpath = '/list'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'rsf.qiniuapi.com'; + String queryStr = ''; + + if (query['delimiter'] == null && query['prefix'] == null) { + queryStr = 'bucket=$bucket&limit=1000'; + } else if (query['delimiter'] == null && query['prefix'] != null) { + queryStr = 'bucket=$bucket&limit=1000&prefix=${Uri.encodeComponent(query['prefix'])}'; + } else if (query['delimiter'] != null && query['prefix'] == null) { + queryStr = 'bucket=$bucket&limit=1000&delimiter=${Uri.encodeComponent(query['delimiter'])}'; + } else { + queryStr = + 'bucket=$bucket&limit=1000&prefix=${Uri.encodeComponent(query['prefix'])}&delimiter=${Uri.encodeComponent(query['delimiter'])}'; + } - String authorization = await qiniuAuthorization( - method, urlpath, queryStr, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + String authorization = await qiniuAuthorization( + method, urlpath, queryStr, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); - try { String marker = ''; String newQuery = queryStr; var response = await dio.get( @@ -672,20 +609,7 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({'element': element, 'query': query}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({'element': element, 'query': query}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'query': query}, "QiniuManageAPI", "queryBucketFiles"); return [e.toString()]; } } @@ -711,28 +635,28 @@ signingStr=signingStr+ //新建文件夹 static createFolder(Map element, String prefix, String newfolder) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String area = element['area']; - String host = QiniuImageUploadUtils.areaHostMap[area]!; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String urlSafeBase64EncodePutPolicy = - QiniuImageUploadUtils.geturlSafeBase64EncodePutPolicy(bucket, prefix, newfolder); - String uploadToken = QiniuImageUploadUtils.getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); - - String urlpath = '$prefix$newfolder'; - FormData formData = FormData.fromMap({ - "key": urlpath, - "fileName": newfolder, - "token": uploadToken, - "file": '', - }); + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String area = element['area']; + String host = QiniuImageUploadUtils.areaHostMap[area]!; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String urlSafeBase64EncodePutPolicy = + QiniuImageUploadUtils.geturlSafeBase64EncodePutPolicy(bucket, prefix, newfolder); + String uploadToken = QiniuImageUploadUtils.getUploadToken(accessKey, secretKey, urlSafeBase64EncodePutPolicy); - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); + String urlpath = '$prefix$newfolder'; + FormData formData = FormData.fromMap({ + "key": urlpath, + "fileName": newfolder, + "token": uploadToken, + "file": '', + }); + + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); - try { var response = await dio.post( host, data: formData, @@ -744,50 +668,35 @@ signingStr=signingStr+ ); if (response.statusCode == 200) { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "createFolder", - text: formatErrorMessage({'element': element, 'prefix': prefix, 'newfolder': newfolder}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "createFolder", - text: formatErrorMessage({'element': element, 'prefix': prefix, 'newfolder': newfolder}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'prefix': prefix, 'newfolder': newfolder}, "QiniuManageAPI", "createFolder"); return [e.toString()]; } } //删除文件 static deleteFile(Map element, String key) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - - String method = 'DELETE'; - String urlpath = '/delete'; - String encodeEntryURI = urlSafeBase64Encode(utf8.encode('$bucket:$key')); - urlpath = '$urlpath/$encodeEntryURI'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'rs.qiniuapi.com'; - String authorization = await qiniuAuthorization( - method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); - - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + + String method = 'DELETE'; + String urlpath = '/delete'; + String encodeEntryURI = urlSafeBase64Encode(utf8.encode('$bucket:$key')); + urlpath = '$urlpath/$encodeEntryURI'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'rs.qiniuapi.com'; + String authorization = await qiniuAuthorization( + method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); + var response = await dio.delete( 'https://$host$urlpath', options: Options( @@ -800,27 +709,12 @@ signingStr=signingStr+ ); if (response.statusCode == 200) { - return [ - 'success', - ]; + return ['success']; } else { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'element': element, 'key': key}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'element': element, 'key': key}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'key': key}, "QiniuManageAPI", "deleteFile"); return [e.toString()]; } } @@ -874,25 +768,25 @@ signingStr=signingStr+ //复制/移动/重命名文件 static copyFile(String operateType, Map element, String key, String newKey, bool isCover) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - - String method = 'POST'; - String entryURISrc = '$bucket:$key'; - String entryURIDest = '$bucket:$newKey'; - String encodeEntryURI = urlSafeBase64Encode(utf8.encode(entryURISrc)); - String encodeEntryURIDest = urlSafeBase64Encode(utf8.encode(entryURIDest)); - String urlpath = '/$operateType/$encodeEntryURI/$encodeEntryURIDest/force/$isCover'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String host = 'rs.qiniuapi.com'; - String authorization = await qiniuAuthorization( - method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); - - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + + String method = 'POST'; + String entryURISrc = '$bucket:$key'; + String entryURIDest = '$bucket:$newKey'; + String encodeEntryURI = urlSafeBase64Encode(utf8.encode(entryURISrc)); + String encodeEntryURIDest = urlSafeBase64Encode(utf8.encode(entryURIDest)); + String urlpath = '/$operateType/$encodeEntryURI/$encodeEntryURIDest/force/$isCover'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String host = 'rs.qiniuapi.com'; + String authorization = await qiniuAuthorization( + method, urlpath, null, host, 'application/x-www-form-urlencoded', null, '', accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); + var response = await dio.post( 'https://$host$urlpath', options: Options( @@ -914,20 +808,7 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "copyFile", - text: formatErrorMessage({'element': element, 'key': key, 'newKey': newKey}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "copyFile", - text: formatErrorMessage({'element': element, 'key': key, 'newKey': newKey}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'key': key, 'newKey': newKey}, "QiniuManageAPI", "copyFile"); if (e.toString().contains('614')) { return ['existed']; } else { @@ -938,31 +819,32 @@ signingStr=signingStr+ //异步第三方资源抓取 static sisyphusFetch(Map element, String bucketPrefix, String link) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['area']; - String host = 'api-$region.qiniuapi.com'; - String contentType = 'application/json'; - String method = 'POST'; - String urlpath = '/sisyphus/fetch'; - String accessKey = configMap['accessKey']; - String secretKey = configMap['secretKey']; - String fileNames = link.split('/').last; - if (fileNames.contains('?')) { - fileNames = fileNames.split('?').first; - } - Map body = { - 'url': link, - 'bucket': bucket, - 'key': bucketPrefix + fileNames, - }; - String bodyString = json.encode(body); - String authorization = - await qiniuAuthorization(method, urlpath, null, host, contentType, null, bodyString, accessKey, secretKey); - - BaseOptions baseoptions = setBaseOptions(); - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['area']; + String host = 'api-$region.qiniuapi.com'; + String contentType = 'application/json'; + String method = 'POST'; + String urlpath = '/sisyphus/fetch'; + String accessKey = configMap['accessKey']; + String secretKey = configMap['secretKey']; + String fileNames = link.split('/').last; + if (fileNames.contains('?')) { + fileNames = fileNames.split('?').first; + } + Map body = { + 'url': link, + 'bucket': bucket, + 'key': bucketPrefix + fileNames, + }; + String bodyString = json.encode(body); + String authorization = + await qiniuAuthorization(method, urlpath, null, host, contentType, null, bodyString, accessKey, secretKey); + + BaseOptions baseoptions = setBaseOptions(); + Dio dio = Dio(baseoptions); + var response = await dio.post( 'https://$host$urlpath', data: body, @@ -982,20 +864,7 @@ signingStr=signingStr+ return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "QiniuManageAPI", - methodName: "sisyphusFetch", - text: formatErrorMessage({'element': element, 'bucketPrefix': bucketPrefix, 'link': link}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "QiniuManageAPI", - methodName: "sisyphusFetch", - text: formatErrorMessage({'element': element, 'bucketPrefix': bucketPrefix, 'link': link}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'bucketPrefix': bucketPrefix, 'link': link}, "QiniuManageAPI", "sisyphusFetch"); return [e.toString()]; } } diff --git a/lib/picture_host_manage/manage_api/smms_manage_api.dart b/lib/picture_host_manage/manage_api/smms_manage_api.dart index 439b9bc..c20f28c 100644 --- a/lib/picture_host_manage/manage_api/smms_manage_api.dart +++ b/lib/picture_host_manage/manage_api/smms_manage_api.dart @@ -15,7 +15,7 @@ class SmmsManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_smms_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_smms_config.txt')); } static Future get _localPath async { @@ -40,23 +40,26 @@ class SmmsManageAPI { static Future getConfigMap() async { String configStr = await readSmmsConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } static getUserProfile() async { - Map configMap = await getConfigMap(); - String token = configMap['token']; + try { + Map configMap = await getConfigMap(); + String token = configMap['token']; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Content-Type': 'multipart/form-data', - }; - Dio dio = Dio(baseoptions); - FormData formData = FormData.fromMap({}); + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Content-Type': 'multipart/form-data', + }; + Dio dio = Dio(baseoptions); + FormData formData = FormData.fromMap({}); - try { var response = await dio.post('${smmsAPIUrl}profile', data: formData); if (response.statusCode == 200 && response.data['success'] == true) { Map userProfile = response.data['data']; @@ -65,38 +68,26 @@ class SmmsManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsManageAPI", - methodName: "getUserProfile", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsManageAPI", - methodName: "getUserProfile", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "SmmsManageAPI", "getUserProfile"); return [e.toString()]; } } static getFileList({required int page}) async { - Map configMap = await getConfigMap(); - String token = configMap['token']; - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': token, - 'Content-Type': 'multipart/form-data', - }; - Dio dio = Dio(baseoptions); - Map params = { - 'page': page, - }; - try { + Map configMap = await getConfigMap(); + String token = configMap['token']; + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': token, + 'Content-Type': 'multipart/form-data', + }; + Dio dio = Dio(baseoptions); + Map params = { + 'page': page, + }; + var response = await dio.get('${smmsAPIUrl}upload_history', queryParameters: params); if (response.statusCode == 200 && response.data['success'] == true) { Map result = response.data; @@ -105,59 +96,33 @@ class SmmsManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsManageAPI", - methodName: "getFileList", - text: formatErrorMessage({'page': page}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsManageAPI", - methodName: "getFileList", - text: formatErrorMessage({'page': page}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'page': page}, "SmmsManageAPI", "getFileList"); return [e.toString()]; } } static uploadFile(String filename, String path) async { - Map configMap = await getConfigMap(); - FormData formdata = FormData.fromMap({ - "smfile": await MultipartFile.fromFile(path, filename: filename), - "format": "json", - }); - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - "Content-Type": "multipart/form-data", - }; - Dio dio = Dio(options); try { + Map configMap = await getConfigMap(); + FormData formdata = FormData.fromMap({ + "smfile": await MultipartFile.fromFile(path, filename: filename), + "format": "json", + }); + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": configMap["token"], + "Content-Type": "multipart/form-data", + }; + Dio dio = Dio(options); + var response = await dio.post('${smmsAPIUrl}upload', data: formdata); if (response.statusCode == 200 && response.data!['success'] == true) { - return [ - "success", - ]; + return ["success"]; } else { return ["failed"]; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'path': path}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'path': path}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'filename': filename, 'path': path}, "SmmsManageAPI", "uploadFile"); return ['error']; } } @@ -222,19 +187,7 @@ class SmmsManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'fileLink': fileLink}, "SmmsManageAPI", "uploadNetworkFile"); return ['failed']; } } @@ -269,20 +222,20 @@ class SmmsManageAPI { } static deleteFile(String hash) async { - Map configMap = await getConfigMap(); - Map formdata = { - "hash": hash, - "format": "json", - }; - - BaseOptions options = setBaseOptions(); - options.headers = { - "Authorization": configMap["token"], - }; - Dio dio = Dio(options); - String deleteUrl = "${smmsAPIUrl}delete/$hash"; - try { + Map configMap = await getConfigMap(); + Map formdata = { + "hash": hash, + "format": "json", + }; + + BaseOptions options = setBaseOptions(); + options.headers = { + "Authorization": configMap["token"], + }; + Dio dio = Dio(options); + String deleteUrl = "${smmsAPIUrl}delete/$hash"; + var response = await dio.get(deleteUrl, queryParameters: formdata); if (response.statusCode == 200 && response.data!['success'] == true) { return ["success"]; @@ -290,19 +243,7 @@ class SmmsManageAPI { return ["failed"]; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "SmmsManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'hash': hash}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "SmmsManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'hash': hash}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'hash': hash}, "SmmsManageAPI", "deleteFile"); return [e.toString()]; } } diff --git a/lib/picture_host_manage/manage_api/tencent_manage_api.dart b/lib/picture_host_manage/manage_api/tencent_manage_api.dart index c5b786d..0c13bf7 100644 --- a/lib/picture_host_manage/manage_api/tencent_manage_api.dart +++ b/lib/picture_host_manage/manage_api/tencent_manage_api.dart @@ -37,14 +37,13 @@ class TencentManageAPI { 'na-ashburn': '弗吉尼亚(美东)', 'na-toronto': '多伦多', 'sa-saopaulo': '圣保罗', - 'eu-frankfurt': '法兰克福', - 'eu-moscow': '莫斯科', + 'eu-frankfurt': '法兰克福' }; static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_tencent_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_tencent_config.txt')); } static Future get _localPath async { @@ -69,6 +68,9 @@ class TencentManageAPI { static Future getConfigMap() async { String configStr = await readTencentConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -137,27 +139,28 @@ class TencentManageAPI { //获取存储桶列表 static getBucketList() async { - Map configMap = await getConfigMap(); - String method = 'GET'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - - String host = 'service.cos.myqcloud.com'; - - Map header = { - 'Host': 'service.cos.myqcloud.com', - }; - - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': authorization, - 'Host': host, - }; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String method = 'GET'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + + String host = 'service.cos.myqcloud.com'; + + Map header = { + 'Host': 'service.cos.myqcloud.com', + }; + + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': authorization, + 'Host': host, + }; + Dio dio = Dio(baseoptions); + var response = await dio.get('https://$host'); if (response.statusCode == 200) { String responseBody = response.data; @@ -169,69 +172,58 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "TencentManageAPI", "getBucketList"); return [e.toString()]; } } //新建存储桶 static createBucket(Map newBucketConfigMap) async { - Map configMap = await getConfigMap(); - String appId = configMap['appId']; - String bucket = newBucketConfigMap['bucketName']; - String region = newBucketConfigMap['region']; - bool multiAZ = newBucketConfigMap['multiAZ']; - String xCosACL = newBucketConfigMap['xCosACL']; - - if (multiAZ == true && - (region != 'ap-beijing' && region != 'ap-guangzhou') && - (region != 'ap-shanghai' && region != 'ap-singapore')) { - return [ - 'multiAZ error', - ]; - } - var body = 'MAZ'; - var bodyMd5 = md5.convert(utf8.encode(body)); - String base64BodyMd5 = base64.encode(bodyMd5.bytes); - if (!bucket.endsWith('-appId')) { - bucket = '$bucket-$appId'; - } + try { + Map configMap = await getConfigMap(); + String appId = configMap['appId']; + String bucket = newBucketConfigMap['bucketName']; + String region = newBucketConfigMap['region']; + bool multiAZ = newBucketConfigMap['multiAZ']; + String xCosACL = newBucketConfigMap['xCosACL']; + + if (multiAZ == true && + (region != 'ap-beijing' && region != 'ap-guangzhou') && + (region != 'ap-shanghai' && region != 'ap-singapore')) { + return [ + 'multiAZ error', + ]; + } + var body = 'MAZ'; + var bodyMd5 = md5.convert(utf8.encode(body)); + String base64BodyMd5 = base64.encode(bodyMd5.bytes); + if (!bucket.endsWith('-appId')) { + bucket = '$bucket-$appId'; + } + + String method = 'PUT'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + 'x-cos-acl': xCosACL, + }; - String method = 'PUT'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - 'x-cos-acl': xCosACL, - }; - - if (multiAZ == true) { - header['content-type'] = 'application/xml'; - header['content-length'] = body.length.toString(); - header['content-md5'] = base64BodyMd5; - } + if (multiAZ == true) { + header['content-type'] = 'application/xml'; + header['content-length'] = body.length.toString(); + header['content-md5'] = base64BodyMd5; + } - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { Response response; if (multiAZ == true) { response = await dio.put('https://$host', data: body); @@ -245,45 +237,33 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "createBucket", - text: formatErrorMessage({'newBucketConfigMap': newBucketConfigMap}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "createBucket", - text: formatErrorMessage({'newBucketConfigMap': newBucketConfigMap}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'newBucketConfigMap': newBucketConfigMap}, "TencentManageAPI", "createBucket"); return [e.toString()]; } } //删除存储桶 static deleteBucket(Map element) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'DELETE'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String method = 'DELETE'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + var response = await dio.delete('https://$host'); if (response.statusCode == 204) { @@ -292,45 +272,38 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + {'element': element}, + "TencentManageAPI", + "deleteBucket", + ); return [e.toString()]; } } //查询存储桶权限 static queryACLPolicy(Map element) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'GET'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {'acl': ''}); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String method = 'GET'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {'acl': ''}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + var response = await dio.get('https://$host/?acl'); var responseBody = response.data; final myTransformer = Xml2Json(); @@ -343,46 +316,34 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "queryACLPolicy", - text: formatErrorMessage({'element': element}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "queryACLPolicy", - text: formatErrorMessage({'element': element}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element}, "TencentManageAPI", "queryACLPolicy"); return [e.toString()]; } } //更改存储桶权限 static changeACLPolicy(Map element, String newACL) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'PUT'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - 'x-cos-acl': newACL, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {'acl': ''}); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String method = 'PUT'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + 'x-cos-acl': newACL, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {'acl': ''}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + var response = await dio.put('https://$host/?acl'); if (response.statusCode == 200) { return ['success']; @@ -390,20 +351,7 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "changeACLPolicy", - text: formatErrorMessage({'element': element, 'newACL': newACL}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "changeACLPolicy", - text: formatErrorMessage({'element': element, 'newACL': newACL}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'newACL': newACL}, "TencentManageAPI", "changeACLPolicy"); return [e.toString()]; } } @@ -444,28 +392,28 @@ class TencentManageAPI { //查询存储桶文件列表 static queryBucketFiles(Map element, Map query) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'GET'; - String urlpath = '/'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - }; - query['max-keys'] = 1000; - - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, query); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String method = 'GET'; + String urlpath = '/'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + }; + query['max-keys'] = 1000; + + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, query); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + String marker = ''; var response = await dio.get('https://$host', queryParameters: query); var responseBody = response.data; @@ -525,20 +473,7 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({'element': element, 'query': query}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({'element': element, 'query': query}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'query': query}, "TencentManageAPI", "queryBucketFiles"); return [e.toString()]; } } @@ -560,26 +495,26 @@ class TencentManageAPI { //删除文件 static deleteFile(Map element, String key) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'DELETE'; - String urlpath = '/$key'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - Map header = { - 'Host': host, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); - - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; + + String method = 'DELETE'; + String urlpath = '/$key'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + Map header = { + 'Host': host, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); + var response = await dio.delete('https://$host/$key'); if (response.statusCode == 204) { return ['success']; @@ -587,20 +522,7 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'element': element, 'key': key}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({'element': element, 'key': key}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'key': key}, "TencentManageAPI", "deleteFile"); return [e.toString()]; } } @@ -652,57 +574,48 @@ class TencentManageAPI { //重命名文件 static copyFile(Map element, String key, String newKey) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; - - String method = 'PUT'; - - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; - String newName = ''; - if (key.substring(0, key.lastIndexOf('/') + 1) == '') { - newName = newKey; - } else { - newName = key.substring(0, key.lastIndexOf('/') + 1) + newKey; - } - String urlpath = '/$newName'; - String xCosCopySource = '/$bucket.cos.$region.myqcloud.com/${Uri.encodeComponent(key)}'; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; - Map header = { - 'Host': host, - 'x-cos-copy-source': xCosCopySource, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + String method = 'PUT'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + String newName = ''; + if (key.substring(0, key.lastIndexOf('/') + 1) == '') { + newName = newKey; + } else { + newName = key.substring(0, key.lastIndexOf('/') + 1) + newKey; + } + String urlpath = '/$newName'; + String xCosCopySource = '/$bucket.cos.$region.myqcloud.com/${Uri.encodeComponent(key)}'; + + Map header = { + 'Host': host, + 'x-cos-copy-source': xCosCopySource, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - try { var response = await dio.put('https://$host/$newName'); - if (response.statusCode == 200) { - return ['success']; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "copyFile", - text: formatErrorMessage({'element': element, 'key': key, 'newKey': newKey}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "copyFile", - text: formatErrorMessage({'element': element, 'key': key, 'newKey': newKey}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + {'element': element, 'key': key, 'newKey': newKey}, + "TencentManageAPI", + "copyFile", + ); return [e.toString()]; } } @@ -751,28 +664,29 @@ class TencentManageAPI { //下载文件 static downloadFile(Map element, String key, String path) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; - String method = 'GET'; + String method = 'GET'; + + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; + String urlpath = '/$key'; - String urlpath = '/$key'; + Map header = { + 'Host': host, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); - Map header = { - 'Host': host, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { Response response = await dio.download('https://$host/$key', path); if (response.statusCode == 200) { return ['success']; @@ -780,51 +694,44 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "downloadFile", - text: formatErrorMessage({'element': element, 'key': key, 'path': path}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "downloadFile", - text: formatErrorMessage({'element': element, 'key': key, 'path': path}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + {'element': element, 'key': key, 'path': path}, + "TencentManageAPI", + "downloadFile", + ); return [e.toString()]; } } //新建文件夹 static createFolder(Map element, String prefix, String newfolder) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; - String method = 'PUT'; + String method = 'PUT'; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; - String urlpath = '/$prefix$newfolder'; - if (urlpath.substring(urlpath.length - 1) != '/') { - urlpath = '$urlpath/'; - } + String urlpath = '/$prefix$newfolder'; + if (urlpath.substring(urlpath.length - 1) != '/') { + urlpath = '$urlpath/'; + } - Map header = { - 'Host': host, - }; - String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + Map header = { + 'Host': host, + }; + String authorization = tecentAuthorization(method, urlpath, header, secretId, secretKey, {}); + + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = header; + baseoptions.headers['Authorization'] = authorization; + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = header; - baseoptions.headers['Authorization'] = authorization; - Dio dio = Dio(baseoptions); - try { var response = await dio.put('https://$host$urlpath'); if (response.statusCode == 200) { return ['success']; @@ -832,20 +739,7 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "createFolder", - text: formatErrorMessage({'element': element, 'prefix': prefix, 'newfolder': newfolder}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "createFolder", - text: formatErrorMessage({'element': element, 'prefix': prefix, 'newfolder': newfolder}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'prefix': prefix, 'newfolder': newfolder}, "TencentManageAPI", "createFolder"); return [e.toString()]; } } @@ -857,56 +751,57 @@ class TencentManageAPI { String filepath, String prefix, ) async { - Map configMap = await getConfigMap(); - String bucket = element['name']; - String region = element['location']; + try { + Map configMap = await getConfigMap(); + String bucket = element['name']; + String region = element['location']; - String secretId = configMap['secretId']; - String secretKey = configMap['secretKey']; - String host = '$bucket.cos.$region.myqcloud.com'; + String secretId = configMap['secretId']; + String secretKey = configMap['secretKey']; + String host = '$bucket.cos.$region.myqcloud.com'; + + String urlpath = '/$prefix$filename'; + //上传策略 + int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; + int endTimestamp = startTimestamp + 86400; + String keyTime = '$startTimestamp;$endTimestamp'; + Map uploadPolicy = { + "expiration": "2033-03-03T09:38:12.414Z", + "conditions": [ + {"acl": "default"}, + {"bucket": bucket}, + {"key": urlpath}, + {"q-sign-algorithm": "sha1"}, + {"q-ak": secretId}, + {"q-sign-time": keyTime} + ] + }; + String uploadPolicyStr = jsonEncode(uploadPolicy); + String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); + FormData formData = FormData.fromMap({ + 'key': urlpath, + 'policy': base64Encode(utf8.encode(uploadPolicyStr)), + 'acl': 'default', + 'q-sign-algorithm': 'sha1', + 'q-ak': secretId, + 'q-key-time': keyTime, + 'q-sign-time': keyTime, + 'q-signature': singature, + 'file': await MultipartFile.fromFile(filepath, filename: filename), + }); - String urlpath = '/$prefix$filename'; - //上传策略 - int startTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; - int endTimestamp = startTimestamp + 86400; - String keyTime = '$startTimestamp;$endTimestamp'; - Map uploadPolicy = { - "expiration": "2033-03-03T09:38:12.414Z", - "conditions": [ - {"acl": "default"}, - {"bucket": bucket}, - {"key": urlpath}, - {"q-sign-algorithm": "sha1"}, - {"q-ak": secretId}, - {"q-sign-time": keyTime} - ] - }; - String uploadPolicyStr = jsonEncode(uploadPolicy); - String singature = TencentImageUploadUtils.getUploadAuthorization(secretKey, keyTime, uploadPolicyStr); - FormData formData = FormData.fromMap({ - 'key': urlpath, - 'policy': base64Encode(utf8.encode(uploadPolicyStr)), - 'acl': 'default', - 'q-sign-algorithm': 'sha1', - 'q-ak': secretId, - 'q-key-time': keyTime, - 'q-sign-time': keyTime, - 'q-signature': singature, - 'file': await MultipartFile.fromFile(filepath, filename: filename), - }); + BaseOptions baseoptions = setBaseOptions(); + File uploadFile = File(filepath); + String contentLength = await uploadFile.length().then((value) { + return value.toString(); + }); + baseoptions.headers = { + 'Host': host, + 'Content-Type': Global.multipartString, + 'Content-Length': contentLength, + }; + Dio dio = Dio(baseoptions); - BaseOptions baseoptions = setBaseOptions(); - File uploadFile = File(filepath); - String contentLength = await uploadFile.length().then((value) { - return value.toString(); - }); - baseoptions.headers = { - 'Host': host, - 'Content-Type': Global.multipartString, - 'Content-Length': contentLength, - }; - Dio dio = Dio(baseoptions); - try { var response = await dio.post( 'https://$host', data: formData, @@ -917,22 +812,8 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "uploadFile", - text: formatErrorMessage( - {'element': element, 'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "uploadFile", - text: formatErrorMessage( - {'element': element, 'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'element': element, 'filename': filename, 'filepath': filepath, 'prefix': prefix}, + "TencentManageAPI", "uploadFile"); return ['error']; } } @@ -1001,20 +882,8 @@ class TencentManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "TencentManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "TencentManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'element': element, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, {'fileLink': fileLink, 'element': element, 'prefix': prefix}, "TencentManageAPI", "uploadNetworkFile"); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/upyun_manage_api.dart b/lib/picture_host_manage/manage_api/upyun_manage_api.dart index d735902..b74c90c 100644 --- a/lib/picture_host_manage/manage_api/upyun_manage_api.dart +++ b/lib/picture_host_manage/manage_api/upyun_manage_api.dart @@ -25,7 +25,7 @@ class UpyunManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return File('$path/${defaultUser}_upyun_config.txt'); + return ensureFileExists(File('$path/${defaultUser}_upyun_config.txt')); } static Future get _localPath async { @@ -50,6 +50,9 @@ class UpyunManageAPI { static Future getConfigMap() async { String configStr = await readUpyunConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -153,7 +156,7 @@ class UpyunManageAPI { static getUpyunManageConfigMap() async { var queryUpyunManage = await UpyunManageAPI.readUpyunManageConfig(); - if (queryUpyunManage == 'Erorr') { + if (queryUpyunManage == 'Error' || queryUpyunManage == '') { return 'Error'; } else { var jsonResult = jsonDecode(queryUpyunManage); @@ -240,25 +243,12 @@ class UpyunManageAPI { 'https://api.upyun.com/oauth/tokens', data: jsonEncode(params), ); - if (response.statusCode == 200) { - return ['success', response.data]; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success', response.data]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "getToken", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "getToken", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'getToken'); return ['failed']; } } @@ -273,25 +263,12 @@ class UpyunManageAPI { var response = await dio.get( 'https://api.upyun.com/oauth/tokens', ); - if (response.statusCode == 200) { - return ['success']; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "checkToken", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "checkToken", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'checkToken'); return ['failed']; } } @@ -311,46 +288,34 @@ class UpyunManageAPI { queryParameters: params, ); - if (response.statusCode == 200) { - return ['success']; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteToken", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteToken", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'deleteToken'); return ['failed']; } } static getBucketList() async { - var configMap = await getUpyunManageConfigMap(); - if (configMap == 'Error') { - return ['failed']; - } - String token = configMap['token']; - String host = 'https://api.upyun.com/buckets'; - BaseOptions baseoptions = setBaseOptions(); - baseoptions.headers = { - 'Authorization': 'Bearer $token', - }; - Map queryParameters = { - 'limit': 100, - 'bucket_type': 'file', - }; - Dio dio = Dio(baseoptions); try { + var configMap = await getUpyunManageConfigMap(); + if (configMap == 'Error') { + return ['failed']; + } + String token = configMap['token']; + String host = 'https://api.upyun.com/buckets'; + BaseOptions baseoptions = setBaseOptions(); + baseoptions.headers = { + 'Authorization': 'Bearer $token', + }; + Map queryParameters = { + 'limit': 100, + 'bucket_type': 'file', + }; + Dio dio = Dio(baseoptions); + String max = ''; var response = await dio.get( host, @@ -385,19 +350,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "getBucketList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'getBucketList'); return [e.toString()]; } } @@ -424,25 +377,12 @@ class UpyunManageAPI { host, queryParameters: params, ); - if (response.statusCode == 200) { - return ['success', response.data]; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success', response.data]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "getBucketInfo", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "getBucketInfo", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'getBucketInfo'); return [e.toString()]; } } @@ -470,25 +410,12 @@ class UpyunManageAPI { host, data: params, ); - if (response.statusCode == 200) { - return ['success']; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteBucket", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'deleteBucket'); return [e.toString()]; } } @@ -515,27 +442,12 @@ class UpyunManageAPI { host, data: params, ); - if (response.statusCode == 201) { - return ['success']; - } else { + if (response.statusCode != 201) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "putBucket", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "putBucket", - text: formatErrorMessage({ - 'bucketName': bucketName, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'putBucket'); return [e.toString()]; } } @@ -561,25 +473,12 @@ class UpyunManageAPI { host, queryParameters: params, ); - if (response.statusCode == 200) { - return ['success', response.data['operators']]; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success', response.data['operators']]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "getOperator", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "getOperator", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'getOperator'); return [e.toString()]; } } @@ -606,27 +505,12 @@ class UpyunManageAPI { host, data: params, ); - if (response.statusCode == 201) { - return [ - 'success', - ]; - } else { + if (response.statusCode != 201) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "putOperator", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "putOperator", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'addOperator'); return [e.toString()]; } } @@ -653,27 +537,12 @@ class UpyunManageAPI { host, queryParameters: params, ); - if (response.statusCode == 200) { - return [ - 'success', - ]; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteOperator", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteOperator", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, 'UpyunManageAPI', 'deleteOperator'); return [e.toString()]; } } @@ -705,6 +574,8 @@ class UpyunManageAPI { String options = textMap['option']; String path = textMap['path']; + String antiLeechToken = textMap['antiLeechToken']; + String antiLeechExpire = textMap['antiLeechExpire']; if (path.isEmpty || path.replaceAll(' ', '').isEmpty) { path = 'None'; } else { @@ -716,7 +587,8 @@ class UpyunManageAPI { } } - final upyunConfig = UpyunConfigModel(bucket, operatorName, operatorPassword, url, options, path); + final upyunConfig = + UpyunConfigModel(bucket, operatorName, operatorPassword, url, options, path, antiLeechToken, antiLeechExpire); final upyunConfigJson = jsonEncode(upyunConfig); final upyunConfigFile = await _localFile; await upyunConfigFile.writeAsString(upyunConfigJson); @@ -778,23 +650,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({ - 'prefix': prefix, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "queryBucketFiles", - text: formatErrorMessage({ - 'prefix': prefix, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'prefix': prefix}, 'UpyunManageAPI', 'queryBucketFiles'); return [e.toString()]; } } @@ -845,25 +701,14 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "createFolder", - text: formatErrorMessage({ - 'prefix': prefix, - 'newfolder': newfolder, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "createFolder", - text: formatErrorMessage({ - 'prefix': prefix, - 'newfolder': newfolder, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'prefix': prefix, + 'newfolder': newfolder, + }, + 'UpyunManageAPI', + 'createFolder'); return [e.toString()]; } } @@ -892,33 +737,20 @@ class UpyunManageAPI { Dio dio = Dio(baseoptions); try { var response = await dio.delete(url); - if (response.statusCode == 200) { - return [ - 'success', - ]; - } else { + if (response.statusCode != 200) { return ['failed']; } + return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({ - 'prefix': prefix, - 'key': key, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({ - 'prefix': prefix, - 'key': key, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError( + e, + { + 'prefix': prefix, + 'key': key, + }, + 'UpyunManageAPI', + 'deleteFile'); + return [e.toString()]; } } @@ -963,23 +795,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteFolder", - text: formatErrorMessage({ - 'prefix': prefix, - }, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "deleteFolder", - text: formatErrorMessage({ - 'prefix': prefix, - }, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'prefix': prefix}, 'UpyunManageAPI', 'deleteFolder'); return ['failed']; } } @@ -993,6 +809,8 @@ class UpyunManageAPI { String operatorPassword = element['password']; String url = element['url']; String options = configMap['options']; + String antiLeechToken = configMap['antiLeechToken']; + String antiLeechExpire = configMap['antiLeechExpire']; String path = ''; if (folder == null) { path = configMap['path']; @@ -1010,7 +828,8 @@ class UpyunManageAPI { } } - final upyunConfig = UpyunConfigModel(bucket, operatorName, operatorPassword, url, options, path); + final upyunConfig = + UpyunConfigModel(bucket, operatorName, operatorPassword, url, options, path, antiLeechToken, antiLeechExpire); final upyunConfigJson = jsonEncode(upyunConfig); final upyunConfigFile = await _localFile; await upyunConfigFile.writeAsString(upyunConfigJson); @@ -1060,20 +879,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "renameFile", - text: formatErrorMessage({'prefix': prefix, 'key': key, 'newKey': newKey}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "renameFile", - text: formatErrorMessage({'prefix': prefix, 'key': key, 'newKey': newKey}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'prefix': prefix, 'key': key, 'newKey': newKey}, 'UpyunManageAPI', 'renameFile'); return [e.toString()]; } } @@ -1164,20 +970,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'filename': filename, 'filepath': filepath, 'prefix': prefix}, 'UpyunManageAPI', 'uploadFile'); return ['error']; } } @@ -1209,20 +1002,7 @@ class UpyunManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "UpyunManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "UpyunManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'fileLink': fileLink, 'prefix': prefix}, 'UpyunManageAPI', 'uploadNetworkFile'); return ['failed']; } } diff --git a/lib/picture_host_manage/manage_api/webdav_manage_api.dart b/lib/picture_host_manage/manage_api/webdav_manage_api.dart index 98feea9..003d0f5 100644 --- a/lib/picture_host_manage/manage_api/webdav_manage_api.dart +++ b/lib/picture_host_manage/manage_api/webdav_manage_api.dart @@ -15,7 +15,7 @@ class WebdavManageAPI { static Future get _localFile async { final path = await _localPath; String defaultUser = await Global.getUser(); - return io.File('$path/${defaultUser}_webdav_config.txt'); + return ensureFileExists(io.File('$path/${defaultUser}_webdav_config.txt')); } static Future get _localPath async { @@ -40,6 +40,9 @@ class WebdavManageAPI { static Future getConfigMap() async { String configStr = await readWebdavConfig(); + if (configStr == '') { + return {}; + } Map configMap = json.decode(configStr); return configMap; } @@ -70,9 +73,8 @@ class WebdavManageAPI { } static getFileList(String path) async { - webdav.Client client = await getWebdavClient(); - try { + webdav.Client client = await getWebdavClient(); var response = await client.readDir(path); List fileList = []; for (var item in response) { @@ -89,65 +91,29 @@ class WebdavManageAPI { } return ['success', fileList]; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "getFileList", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "getFileList", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "WebdavManageAPI", "getFileList"); return [e.toString()]; } } static createDir(String path) async { - webdav.Client client = await getWebdavClient(); try { + webdav.Client client = await getWebdavClient(); await client.mkdirAll(path); return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "createDir", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "createDir", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "WebdavManageAPI", "createDir"); return [e.toString()]; } } static deleteFile(String path) async { - webdav.Client client = await getWebdavClient(); try { + webdav.Client client = await getWebdavClient(); await client.remove(path); return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "deleteFile", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "WebdavManageAPI", "deleteFile"); return [e.toString()]; } } @@ -156,24 +122,12 @@ class WebdavManageAPI { String path, String newName, ) async { - webdav.Client client = await getWebdavClient(); try { + webdav.Client client = await getWebdavClient(); await client.rename(path, newName, true); return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "renameFile", - text: formatErrorMessage({}, e.toString(), isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "renameFile", - text: formatErrorMessage({}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {}, "WebdavManageAPI", "renameFile"); return [e.toString()]; } } @@ -217,20 +171,7 @@ class WebdavManageAPI { await client.writeFromFile(filepath, prefix + filename); return ['success']; } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "uploadFile", - text: formatErrorMessage({'filename': filename, 'filepath': filepath, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'filename': filename, 'filepath': filepath, 'prefix': prefix}, "WebdavManageAPI", "uploadFile"); return ['error']; } } @@ -261,20 +202,7 @@ class WebdavManageAPI { return ['failed']; } } catch (e) { - if (e is DioException) { - FLog.error( - className: "WebdavManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'prefix': prefix}, e.toString(), - isDioError: true, dioErrorMessage: e), - dataLogType: DataLogType.ERRORS.toString()); - } else { - FLog.error( - className: "WebdavManageAPI", - methodName: "uploadNetworkFile", - text: formatErrorMessage({'fileLink': fileLink, 'prefix': prefix}, e.toString()), - dataLogType: DataLogType.ERRORS.toString()); - } + flogError(e, {'fileLink': fileLink, 'prefix': prefix}, "WebdavManageAPI", "uploadNetworkFile"); return ['failed']; } } diff --git a/lib/picture_host_manage/qiniu/download_api/qiniu_downloader.dart b/lib/picture_host_manage/qiniu/download_api/qiniu_downloader.dart index c7e46ce..9e1768b 100644 --- a/lib/picture_host_manage/qiniu/download_api/qiniu_downloader.dart +++ b/lib/picture_host_manage/qiniu/download_api/qiniu_downloader.dart @@ -108,12 +108,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/qiniu/qiniu_bucket_list_page.dart b/lib/picture_host_manage/qiniu/qiniu_bucket_list_page.dart index 04f1d3a..02e2844 100644 --- a/lib/picture_host_manage/qiniu/qiniu_bucket_list_page.dart +++ b/lib/picture_host_manage/qiniu/qiniu_bucket_list_page.dart @@ -425,7 +425,7 @@ class QiniuBucketListState extends loading_state.BaseLoadingPageState { _saveuserpasswd() async { try { var queryUpyunManage = await UpyunManageAPI.readUpyunManageConfig(); - if (queryUpyunManage == 'Error') { + if (queryUpyunManage == 'Error' || queryUpyunManage == '') { var getTokenResult = await UpyunManageAPI.getToken(_userNametext.text, _passwordcontroller.text); if (getTokenResult[0] == 'success') { String token = getTokenResult[1]['access_token']; @@ -136,7 +136,6 @@ class UpyunLogInState extends State { padding: const EdgeInsets.only(left: 15, right: 15, top: 15), child: TextFormField( controller: _passwordcontroller, - obscureText: true, obscuringCharacter: '*', decoration: const InputDecoration( hintText: '请输入又拍云密码', diff --git a/lib/picture_host_manage/webdav/download_api/webdav_downloader.dart b/lib/picture_host_manage/webdav/download_api/webdav_downloader.dart index 3590438..34812bd 100644 --- a/lib/picture_host_manage/webdav/download_api/webdav_downloader.dart +++ b/lib/picture_host_manage/webdav/download_api/webdav_downloader.dart @@ -146,12 +146,6 @@ class DownloadManager { var task = getDownload(url)!; if (task.status.value != DownloadStatus.canceled && task.status.value != DownloadStatus.paused) { setStatus(task, DownloadStatus.failed); - runningTasks--; - - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } diff --git a/lib/picture_host_manage/webdav/upload_api/webdav_upload_utils.dart b/lib/picture_host_manage/webdav/upload_api/webdav_upload_utils.dart index 750bc91..3e8fc65 100644 --- a/lib/picture_host_manage/webdav/upload_api/webdav_upload_utils.dart +++ b/lib/picture_host_manage/webdav/upload_api/webdav_upload_utils.dart @@ -72,11 +72,6 @@ class UploadManager { var task = getUpload(fileName)!; if (task.status.value != UploadStatus.canceled && task.status.value != UploadStatus.completed) { setStatus(task, UploadStatus.failed); - runningTasks--; - if (_queue.isNotEmpty) { - _startExecution(); - } - rethrow; } } runningTasks--; diff --git a/lib/utils/common_functions.dart b/lib/utils/common_functions.dart index a43a6a8..fed9c67 100644 --- a/lib/utils/common_functions.dart +++ b/lib/utils/common_functions.dart @@ -22,6 +22,21 @@ import 'package:horopic/router/routers.dart'; import 'package:horopic/utils/permission.dart'; import 'package:horopic/picture_host_configure/configure_store/configure_store_file.dart'; +Map psNameTranslate = { + 'aliyun': '阿里云', + 'qiniu': '七牛云', + 'tencent': '腾讯云', + 'upyun': '又拍云', + 'aws': 'S3兼容平台', + 'ftp': 'FTP', + 'github': 'GitHub', + 'sm.ms': 'SM.MS', + 'imgur': 'Imgur', + 'lsky.pro': '兰空图床', + 'alist': 'Alist V3', + 'webdav': 'WebDAV', +}; + Map downloadStatus = { 'DownloadStatus.downloading': "下载中", 'DownloadStatus.paused': "暂停", @@ -40,6 +55,26 @@ Map uploadStatus = { 'UploadStatus.paused': "暂停", }; +flogError(Object e, Map parameters, String className, String methodName) { + FLog.error( + className: className, + methodName: methodName, + text: e is DioException + ? formatErrorMessage(parameters, e.toString(), isDioError: true, dioErrorMessage: e) + : formatErrorMessage(parameters, e.toString()), + dataLogType: DataLogType.ERRORS.toString()); +} + +Future ensureFileExists(File file) async { + bool exists = await file.exists(); + + if (!exists) { + await file.create(recursive: true); + } + + return file; +} + //默认图床参数和配置文件名对应关系 String getpdconfig(String defaultConfig) { return defaultConfig == 'lsky.pro' @@ -327,7 +362,7 @@ String randomStringGenerator(int length) { String renameFileWithTimestamp() { var now = DateTime.now(); var timestamp = now.millisecondsSinceEpoch; - var newFileName = timestamp.toString() + randomStringGenerator(5); + var newFileName = timestamp.toString(); return newFileName; } @@ -362,7 +397,7 @@ renamePictureWithCustomFormat(File file) async { String yearTwoDigit = yearFourDigit.substring(2, 4); String month = DateTime.now().month.toString(); String day = DateTime.now().day.toString(); - String timestampSecond = (DateTime.now().millisecondsSinceEpoch / 1000).floor().toString(); + String timestampMilliSecond = (DateTime.now().millisecondsSinceEpoch).floor().toString(); String uuidWithoutDash = const Uuid().v4().replaceAll('-', ''); String randommd5 = md5.convert(utf8.encode(uuidWithoutDash)).toString(); String randommd5Short = randommd5.substring(0, 16); @@ -374,7 +409,7 @@ renamePictureWithCustomFormat(File file) async { .replaceAll('{y}', yearTwoDigit) .replaceAll('{m}', month) .replaceAll('{d}', day) - .replaceAll('{timestamp}', timestampSecond) + .replaceAll('{timestamp}', timestampMilliSecond) .replaceAll('{uuid}', uuidWithoutDash) .replaceAll('{md5}', randommd5) .replaceAll('{md5-16}', randommd5Short) @@ -395,19 +430,19 @@ String generateUrl(String rawUrl, String fileName) { String generateHtmlFormatedUrl(String rawUrl, String fileName) { String encodedUrl = Global.isURLEncode ? Uri.encodeFull(rawUrl) : rawUrl; - return '$fileName'; + return '${my_path.basename(fileName)}'; } String generateMarkdownFormatedUrl(String rawUrl, String fileName) { String encodedUrl = Global.isURLEncode ? Uri.encodeFull(rawUrl) : rawUrl; - return '![$fileName]($encodedUrl)'; + return '![${my_path.basename(fileName)}]($encodedUrl)'; } String generateMarkdownWithLinkFormatedUrl(String rawUrl, String fileName) { if (Global.isURLEncode) { rawUrl = Uri.encodeFull(rawUrl); } - String markdownWithLinkFormatedUrl = '[![$fileName]($rawUrl)]($rawUrl)'; + String markdownWithLinkFormatedUrl = '[![${my_path.basename(fileName)}]($rawUrl)]($rawUrl)'; return markdownWithLinkFormatedUrl; } @@ -418,7 +453,7 @@ String generateBBcodeFormatedUrl(String url, String fileName) { String generateCustomFormatedUrl(String url, String filename) { String encodeUrl = Global.isURLEncode ? Uri.encodeFull(url) : url; - return Global.customLinkFormat.replaceAll(r'$fileName', filename).replaceAll(r'$url', encodeUrl); + return Global.customLinkFormat.replaceAll(r'$fileName', my_path.basename(filename)).replaceAll(r'$url', encodeUrl); } String getFileSize(int fileSize) { diff --git a/lib/utils/picbed/upyun.dart b/lib/utils/picbed/upyun.dart new file mode 100644 index 0000000..7692af4 --- /dev/null +++ b/lib/utils/picbed/upyun.dart @@ -0,0 +1,46 @@ +import 'dart:convert'; +import 'package:crypto/crypto.dart'; + +String getUpyunUploadPolicy( + {required String bucket, required String saveKey, required String contentMd5, required String date}) { + Map uploadPolicy = { + 'bucket': bucket, + 'save-key': saveKey, + 'expiration': DateTime.now().millisecondsSinceEpoch + 1800000, + 'date': date, + 'content-md5': contentMd5, + }; + return base64.encode(utf8.encode(json.encode(uploadPolicy))); +} + +String getUpyunUploadAuthHeader( + {required String bucket, + required String saveKey, + required String contentMd5, + required String operator, + required String password, + required String base64Policy, + required String date}) { + String stringToSign = 'POST&/$bucket&$date&$base64Policy&$contentMd5'; + String passwordMd5 = md5.convert(utf8.encode(password)).toString(); + String signature = base64.encode(Hmac(sha1, utf8.encode(passwordMd5)).convert(utf8.encode(stringToSign)).bytes); + return 'UPYUN $operator:$signature'; +} + +String getUpyunAntiLeechParam( + {required String saveKey, required String antiLeechToken, required String antiLeechExpiration}) { + String key = ''; + if (saveKey.startsWith('/')) { + key = saveKey; + } else { + key = '/$saveKey'; + } + if (antiLeechToken == '') { + return ''; + } + int dateNowInSecond = (DateTime.now().millisecondsSinceEpoch / 1000).floor(); + int expire = antiLeechExpiration == '' ? dateNowInSecond + 3600 : dateNowInSecond + int.parse(antiLeechExpiration); + String sign = md5.convert(utf8.encode('$antiLeechToken&$expire&$key')).toString(); + String upt = '_upt=${sign.substring(12, 20)}$expire'; + return upt; +} diff --git a/lib/utils/uploader.dart b/lib/utils/uploader.dart index 66c73cc..df9dc13 100644 --- a/lib/utils/uploader.dart +++ b/lib/utils/uploader.dart @@ -27,30 +27,20 @@ Future get _localFile async { String defaultConfig = await Global.getPShost(); String defaultUser = await Global.getUser(); - return File('${directory.path}/${defaultUser}_${getpdconfig(defaultConfig)}.txt'); + return ensureFileExists(File('${directory.path}/${defaultUser}_${getpdconfig(defaultConfig)}.txt')); } //读取图床配置文件 Future readPictureHostConfig() async { - try { - final file = await _localFile; - String contents = await file.readAsString(); - return contents; - } catch (e) { - flogErr( - e, - {}, - 'Uploader', - "readPictureHostConfig", - ); - return "Error"; - } + final file = await _localFile; + String contents = await file.readAsString(); + return contents; } uploaderentry({required String path, required String name}) async { String configData = await readPictureHostConfig(); - if (configData == "Error") { - return ["Error"]; + if (configData == '') { + return ["failed"]; } Map configMap = jsonDecode(configData); String defaultConfig = await Global.getPShost(); @@ -58,12 +48,6 @@ uploaderentry({required String path, required String name}) async { var result = await uploadFunc[defaultConfig]!(path: path, name: name, configMap: configMap); return result; } catch (e) { - flogErr( - e, - {'path': path, 'name': name}, - 'Uploader', - "uploaderentry", - ); - return ["Error"]; + return ["failed"]; } } diff --git a/pubspec.lock b/pubspec.lock index c0196d2..0e52295 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1202,6 +1202,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + universal_io: + dependency: "direct main" + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 0656c27..50a1562 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.1.3+1 +version: 2.2.2+1 environment: sdk: ^3.0.0 @@ -47,6 +47,7 @@ dependencies: r_upgrade: ^0.4.2 extended_image: ^8.1.0 pull_to_refresh: 2.0.0 + universal_io: ^2.2.2 sqflite: ^2.3.0 msh_checkbox: ^2.0.1 flutter_speed_dial: ^7.0.0