import 'dart:io'; import 'package:dio/dio.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; class AttachmentFile { final String path; final String name; final int sizeBytes; final String extension; final String mimeType; const AttachmentFile({ required this.path, required this.name, required this.sizeBytes, required this.extension, required this.mimeType, }); /// 根据扩展名判断是否为图片 bool get isImage { const imageExts = {'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'}; return imageExts.contains(extension); } /// 从本地路径创建(读取文件大小) static Future fromPath(String path) async { final name = _extractName(path); final ext = _extractExt(name); final size = await File(path).length(); return AttachmentFile( path: path, name: name, sizeBytes: size, extension: ext, mimeType: _inferMimeType(ext), ); } /// 从 FilePicker 的 PlatformFile 创建 factory AttachmentFile.fromPlatformFile(PlatformFile file) { final name = file.name; final ext = _extractExt(name); return AttachmentFile( path: file.path ?? '', name: name, sizeBytes: file.size, extension: ext, mimeType: _inferMimeType(ext), ); } /// 从 ImagePicker 的 XFile 创建 static Future fromXFile(XFile xfile) async { final name = _extractName(xfile.path); final ext = _extractExt(name); final size = await xfile.length(); return AttachmentFile( path: xfile.path, name: name, sizeBytes: size, extension: ext, mimeType: _inferMimeType(ext), ); } /// 转换为 Dio MultipartFile,可直接用于 FormData 上传 Future toMultipartFile({String fieldName = 'files'}) { return MultipartFile.fromFile( path, filename: name, contentType: DioMediaType.parse(mimeType), ); } /// 文件大小(MB) double get sizeMB => sizeBytes / (1024 * 1024); // ── 私有工具方法 ── static String _extractName(String path) { return path.split('/').last.split('\\').last; } static String _extractExt(String name) { return name.split('.').last.toLowerCase(); } static String _inferMimeType(String ext) { switch (ext) { case 'jpg': case 'jpeg': return 'image/jpeg'; case 'png': return 'image/png'; case 'gif': return 'image/gif'; case 'bmp': return 'image/bmp'; case 'webp': return 'image/webp'; case 'heic': return 'image/heic'; case 'pdf': return 'application/pdf'; case 'doc': return 'application/msword'; case 'docx': return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; case 'xls': return 'application/vnd.ms-excel'; case 'xlsx': return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; case 'ppt': return 'application/vnd.ms-powerpoint'; case 'pptx': return 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; case 'txt': return 'text/plain'; default: return 'application/octet-stream'; } } }