檔案與圖片
im.files 負責檔案的上傳和下載:上傳頭像、群檔案,或先上傳再自己組織訊息;把訊息中的檔案地址換成可以載入的下載地址,或下載到本地;檢視和刪除群檔案。deeprespond_im_flutter 另外提供顯示圖片的 DRImage 和錄製語音的 VoiceRecorder。
傳送圖片、語音、影片和檔案訊息時不需要先呼叫本頁的方法:把檔案直接交給 im.messages.send(),SDK 會完成上傳前的處理、上傳和傳送,App 被殺後重新啟動也會繼續上傳,見訊息。本頁的方法用於訊息之外的場景。
檔案的用途、大小上限、保留時間、格式檢查等規則與服務端相同,見服務端的檔案概述。
| 要做什麼 | 看哪裡 |
|---|---|
| 上傳頭像、群檔案,顯示進度、取消 | 上傳檔案、取消上傳 |
| 上傳前預覽壓縮後的圖片、取影片封面 | 單獨使用處理結果 |
| 顯示訊息中的圖片 | 用 DRImage 顯示圖片 |
| 播放影片和語音、把檔案儲存到本地 | 檔案地址與下載地址、下載到本地 |
| 群檔案的列表、上傳和刪除 | 群檔案 |
| 按住說話 | 錄製語音 |
檔案的用途與上限
每次上傳都要指定用途 purpose(常量見 UploadPurpose),它決定允許的型別、大小上限和誰能下載:
purpose | 常量 | 用於 | 允許的 kind | 大小上限 | 上傳後得到 |
|---|---|---|---|---|---|
attachment | UploadPurpose.attachment | 訊息中的圖片、語音、影片、檔案 | image、voice、video、file | 執行策略 max_upload_bytes(預設 100 MB);圖片另不超過 20 MB,語音另不超過 5 MB | 檔案地址,要換取下載地址才能載入 |
user_avatar | UploadPurpose.userAvatar | 本人的頭像 | image | 5 MB | 公開地址,可以直接載入 |
group_avatar | UploadPurpose.groupAvatar | 群頭像 | image | 5 MB | 公開地址 |
chatroom_avatar | UploadPurpose.chatroomAvatar | 聊天室的封面 | image | 5 MB | 公開地址 |
group_file | UploadPurpose.groupFile | 群檔案 | image、video、file | 同 attachment | 檔案地址 |
型別 kind 的常量見 FileKind(FileKind.image、voice、video、file)。
上傳前 SDK 先在本地檢查用途、型別和大小,不符合的不會開始上傳,以 local_validation 拒絕:大小超出時 details['reason'] 為 too_large,details['max_bytes'] 為上限;用途與型別不匹配時為 invalid_purpose。服務端還會檢查應用的儲存額度,額度已滿時以 limit_exceeded(reason 為 storage_quota)拒絕。
應用所在的部署沒有提供檔案服務時(執行配置的 media_url_prefix 為空),上傳以 unsupported(reason 為 media_disabled)拒絕。
本地檔案 DRFile
上傳和傳送的檔案都用 DRFile 表示,有兩種來源:
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
// 本地文件的路径,如相册、文件选择器返回的路径
final photo = DRFile(pickedPath, mimeType: 'image/jpeg');
// 内存中的内容,如截图:必须给出文件名,SDK 先写入临时文件再上传
final shot = DRFile.fromBytes(pngBytes, name: 'screenshot.png', mimeType: 'image/png');- 路徑:SDK 只讀取,不修改也不刪除原檔案。路徑不存在時以
local_validation(reason為invalid_value,field為file)拒絕。 mimeType:可選,用於判斷預設的型別(見下文);省略時按檔案開頭的內容判斷。name:檔名,file型別和群檔案下載時以這個名字儲存;省略時取路徑中的檔名。
上傳檔案
im.files.upload() 立即返回一個上傳任務 UploadTask:result 在上傳完成後得到 FileInfo,progress 發出上傳進度,cancel() 取消上傳。
final task = im.files.upload(
DRFile(pickedPath, mimeType: 'application/pdf'),
purpose: UploadPurpose.attachment,
kind: FileKind.file,
onProgress: (loaded, total) => debugPrint('${(loaded * 100 / total).round()}%'),
);
final info = await task.result;
debugPrint('${info.url} ${info.size}');- 型別:省略
kind時,給出了mimeType的按它的大類判斷:image/*為image,video/*為video,audio/*為voice,其他為file;沒有給出mimeType的按檔案開頭的內容判斷,不認識的為file。 - 檔名:省略
name時用DRFile的名字。SDK 按服務端的規則處理檔名:去掉控制字元、/、\和改變文字方向的字元,超長的截斷到 255 個字元,為空時用預設名(如file.pdf)。 - 進度:
onProgress(loaded, total)和task.progress(廣播流,只收到訂閱之後的進度,上傳結束後關閉)按位元組報告,total是處理後實際上傳的大小(圖片壓縮後可能比原檔案小)。兩者任選其一。 - 返回的
FileInfo中,url是檔案地址(頭像為公開地址),圖片另有縮圖地址thumbnailUrl;size、width、height以它為準。
上傳只得到檔案,不傳送訊息,也不修改資料。設定頭像見使用者、好友與線上狀態和群組,如上傳後呼叫 im.users.updateMe(avatarUrl: info.url)。
取消上傳
呼叫任務的 cancel(),或傳入 cancel: DRCancelToken 後取消這個令牌。result 以 aborted 失敗,SDK 同時通知服務端取消這次上傳。
final token = DRCancelToken();
final task = im.files.upload(DRFile(pickedPath), purpose: UploadPurpose.attachment, cancel: token);
// 用户点击“取消”时:task.cancel() 或 token.cancel()
task.cancel();
try {
await task.result;
} on DRException catch (e) {
if (e.code == LocalErrorCode.aborted) showToast('已取消');
}大檔案與中斷後的處理
- 不超過 32 MB 的檔案一次上傳;更大的分片上傳,每片 16 MB,同時上傳 3 片。
- 檔案內容直接傳到儲存服務,不經過 IM 服務的介面,請求不帶 IM 的登入憑證。
- 網路中斷:每次上傳內容遇到網路錯誤時自動重試 3 次;完成上傳時服務端報告缺少內容的,SDK 自動補傳缺少的分片後再完成。服務端繁忙、限流(等待時間不超過 60 秒的)時 SDK 等待後重試,
result只在得到最終結果時完成。 - 切到後臺、App 被殺:
upload()的上傳只在記憶體中進行,狀態不儲存。切到後臺後在系統允許的時間內繼續,之後可能因 App 被掛起而失敗;App 被殺後不能續傳,需要重新上傳。沒有完成的上傳由服務端過期後清理(不計入儲存用量)。隨訊息傳送的附件不同:它們儲存在傳送佇列中,App 重新啟動後從中斷處續傳,見訊息。
上傳前的處理
upload() 和傳送訊息時,SDK 在上傳前自動處理檔案。處理用系統能力完成:iOS、macOS 用 ImageIO 和 AVFoundation,Android 用 BitmapFactory、ImageDecoder 和 MediaMetadataRetriever;Windows、Linux 用 Dart 實現的圖片處理(較慢,在獨立的 isolate 中執行)。
| 內容 | 處理 |
|---|---|
| 圖片 | 按 EXIF 的方向轉正;長邊超過 2048 畫素的縮小到 2048;重新編碼為 JPEG(質量 85),有透明通道的編碼為 PNG。重新編碼的結果不含 EXIF 等後設資料(如拍攝地點)。GIF 不壓縮。長邊和質量可以用 MediaOptions 修改 |
圖片(original: true) | JPEG、PNG、GIF、WebP 不縮小、不重新編碼,只去掉 JPEG 的 EXIF(保留方向)和 XMP、PNG 的文字塊,保留顏色配置。服務端不接受的格式(BMP、TIFF、AVIF 等)不縮小地轉為 JPEG 或 PNG |
| HEIC、HEIF | 轉為 JPEG:iOS、macOS,Android 8 及以上。Windows、Linux 不能轉換,不能作為圖片上傳 |
| 影片 | 不轉碼。MP4 和 MOV 去掉拍攝地點等後設資料(需要時複製一份再修改,不改變原檔案,大小不變);讀出寬高和時長,擷取第一幀作為封面(Windows、Linux 不擷取封面) |
| 語音 | 不轉碼,只檢查格式 |
| 檔案 | 原樣上傳 |
格式按檔案開頭的內容判斷,不看副檔名。服務端接受的格式:圖片 JPEG、PNG、GIF、WebP;影片 MP4、MOV、WebM、3GP;語音 AAC、M4A、MP3、AMR、3GP、Ogg、WAV、WebM。
修改圖片壓縮的引數(建立客戶端時):
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
final client = await DRClient.create(DRClientOptions(
appKey: '1575529652#demo',
apiUrl: Uri.parse('https://im.example.com'),
platform: DRFlutterPlatform.instance,
media: const MediaOptions(imageMaxSide: 1600, jpegQuality: 80),
));以下情況不能按請求的型別上傳,upload() 以 local_validation 拒絕,由你決定是否改為 kind: FileKind.file 重新上傳(隨訊息傳送的附件由 SDK 自動改為檔案訊息):
reason | 情況 |
|---|---|
image_too_large | 圖片超過 5000 萬畫素,或處理後仍超過 20 MB |
unsupported_type | 格式不被服務端接受,如 MKV、AVI 影片,或不能轉換的 HEIC |
Future<FileInfo> uploadImage(DRClient im, DRFile file) async {
try {
return await im.files.upload(file, purpose: UploadPurpose.attachment, kind: FileKind.image).result;
} on DRException catch (e) {
if (e.code == LocalErrorCode.localValidation && (e.reason == 'image_too_large' || e.reason == 'unsupported_type')) {
return im.files.upload(file, purpose: UploadPurpose.attachment, kind: FileKind.file).result; // 作为文件上传
}
rethrow;
}
}隨訊息傳送的附件
用 im.messages.send() 傳送圖片、語音、影片和檔案時,附件按同樣的規則處理,另有以下不同:
- 自動改為檔案訊息:圖片超過 5000 萬畫素或處理後仍超過 20 MB、不能轉換的 HEIC、服務端不接受的圖片、影片和語音格式,不會以
local_validation拒絕,而是自動作為file訊息傳送。 - 附件的儲存:寫入傳送佇列時,SDK 把附件複製到自己的目錄中(
DRFile.fromBytes的內容寫成檔案),所以send()返回後可以刪除原檔案,App 被殺後重新啟動也能從中斷處繼續上傳。超過OutboxOptions.persistAttachmentMaxBytes(預設 200 MB)的附件不復制,只記下原檔案的路徑;如果原檔案在發出之前被刪除或移動,這條訊息以send_abandoned(reason為attachment_lost)失敗,需要使用者重新選擇檔案。 - 傳送成功或刪除訊息(
im.messages.discard())後,SDK 刪除自己儲存的副本。
final client = await DRClient.create(DRClientOptions(
appKey: '1575529652#demo',
apiUrl: Uri.parse('https://im.example.com'),
platform: DRFlutterPlatform.instance,
outbox: const OutboxOptions(persistAttachmentMaxBytes: 500 * 1024 * 1024), // 不超过 500 MB 的附件都复制
));訊息中的附件欄位和傳送方法見訊息。
單獨使用處理結果
im.files.prepare() 只做上面的處理,不上傳,也不需要登入。可以用它預覽壓縮後的圖片、取得影片的封面和時長,或在上傳前判斷是否要改為檔案:
final workDir = await Directory.systemTemp.createTemp('preview_');
try {
final prepared = await im.files.prepare(DRFile(pickedPath), FileKind.video, workDir: workDir.path);
if (prepared.kind != FileKind.video) {
showToast('这个视频格式不能在线播放,将作为文件发送');
} else {
debugPrint('${prepared.width}x${prepared.height},${prepared.durationSeconds} 秒,封面 ${prepared.thumbnailPath}');
}
} finally {
await workDir.delete(recursive: true); // 处理后的文件由你删除
}prepare()不會拒絕不能按請求型別處理的檔案,而是在結果的kind中給出建議的型別(file),downgraded說明原因。- 處理後的檔案寫在
workDir中,省略時在系統臨時目錄中新建一個,用完後由你刪除。不需要改寫的檔案(如不改動的影片、語音和檔案),path就是原檔案的路徑,所以請刪除workDir,不要直接刪除path。 upload()不會上傳影片的封面;需要封面時,把thumbnailPath以kind: FileKind.image另外上傳。
顯示與下載檔案
檔案地址與下載地址
訊息附件和群檔案的地址(檔案地址)不能直接載入,要先換成短期有效的下載地址。頭像的公開地址、你自己的外部地址可以直接使用。im.files.isFileUrl(url) 判斷一個地址是不是需要換取的檔案地址:
Future<String> playableUrl(DRClient im, String url) async {
if (!im.files.isFileUrl(url)) return url;
final resolved = await im.files.resolveUrl(url);
return resolved.downloadUrl; // 交给视频、音频播放器
}- 縮圖:圖片的縮圖地址(檔案地址加
/thumb,即FileInfo的thumbnailUrl和圖片訊息body中的thumbnail_url)同樣要換取,得到長邊不超過 480 畫素的縮圖。 - 有效期:下載地址的有效期為 60 到 90 分鐘,
expiresAt為到期時間。SDK 按到期時間快取(儲存在本地資料庫中,App 重新啟動後仍可使用),到期前 5 分鐘內再呼叫時重新換取。不要把下載地址儲存下來或寫進訊息,儲存不變的檔案地址,每次使用時呼叫resolveUrl。 - 合併請求:20 毫秒內的多次換取合併成一次請求(每次最多 100 個),列表中逐個呼叫
resolveUrl即可。也可以用resolveUrls一次換取多個,每項單獨給出結果。 - 換取每個使用者每分鐘最多 600 個檔案,單項被限流時 SDK 在 5 到 10 秒後自動重試。
- 影片、語音的下載地址支援
Range請求,交給播放器後可以拖動進度。
換取失敗時的錯誤碼:
code | 說明 | 建議的提示 |
|---|---|---|
file_expired | 檔案已過期或已被刪除 | 檔案已過期 |
not_found | 地址無效,或檔案已刪除很久 | 檔案已過期 |
file_blocked | 檔案因違規被遮蔽 | 檔案已被遮蔽 |
not_group_member | 群檔案,本人已不是群成員 | 你已不在群中 |
final results = await im.files.resolveUrls(urls);
for (final r in results) {
final error = r.error;
if (error != null) {
debugPrint('${r.url}:${error.code == 'file_blocked' ? '文件已被屏蔽' : '文件已过期'}');
} else {
debugPrint('${r.url} → ${r.downloadUrl}');
}
}用 DRImage 顯示圖片
DRImage 是 Flutter 的 ImageProvider:傳入檔案地址或縮圖地址,它自動換取下載地址、下載並按地址快取在磁碟中;不是檔案地址的(如外部的頭像地址)直接請求。可以用在任何接受 ImageProvider 的地方(Image、CircleAvatar、DecorationImage):
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
import 'package:flutter/material.dart';
class ImageBubble extends StatelessWidget {
const ImageBubble({super.key, required this.im, required this.message});
final DRClient im;
final MessageView message;
@override
Widget build(BuildContext context) {
final body = message.body ?? const {};
final url = (body['thumbnail_url'] ?? body['url']) as String?; // 列表中先显示缩略图
if (url == null) return const SizedBox(width: 120, height: 120);
return Image(
image: DRImage(url, client: im),
width: 200,
fit: BoxFit.cover,
loadingBuilder: (context, child, progress) => progress == null ? child : const SizedBox(width: 200, height: 150, child: Center(child: CircularProgressIndicator())),
errorBuilder: (context, error, stack) => const Icon(Icons.broken_image),
);
}
}- 圖片內容快取在系統快取目錄的
deeprespond_im/images中,超過MediaOptions.imageCacheBytes(預設 200 MB)時按最近使用淘汰到 80%。同一地址同時只下載一次。 - 下載失敗(如檔案已過期、已被遮蔽)時由
errorBuilder顯示,error是DRException。 DRImageCache.instance.clear()清除全部快取,可以在設定頁中提供“清除快取”,或在退出登入時呼叫。- 影片的封面、檔案的預覽同樣可以用
DRImageCache.instance.fetch(im, url)取得快取在本地的檔案。
下載到本地
im.files.download() 把檔案儲存到指定的路徑,可以報告進度、中途取消;檔案地址先換取下載地址,不是檔案地址的直接請求這個地址。請求不帶任何登入憑證。
final token = DRCancelToken();
final saved = await im.files.download(
fileUrl,
'$downloadsDir/$fileName',
onProgress: (loaded, total) => debugPrint(total > 0 ? '${(loaded * 100 / total).round()}%' : '$loaded 字节'),
cancel: token,
);
debugPrint('已保存到 ${saved.path},${saved.size} 字节');- 內容先寫入
savePath.part,完成後改名為savePath;失敗或取消時刪除.part,不會留下不完整的檔案。所在的目錄不存在時自動建立。 - 儲存的位置由你決定,如
path_provider的應用文件目錄;SDK 不清理下載的檔案。
群檔案
群檔案是群成員共享的檔案,預設不過期(應用可以設定保留天數),群解散時全部刪除。群成員可以上傳;上傳者本人、群主和管理員可以刪除。應用在執行策略中關閉了群檔案(執行配置的 group_file_enabled 為 false)時,客戶端不能上傳,upload() 以 permission_denied(reason 為 group_file_disabled)拒絕。
// 上传:purpose 为 group_file,必须带 groupId;不能是语音
final info = await im.files
.upload(DRFile(pickedPath), purpose: UploadPurpose.groupFile, groupId: groupId, name: '会议纪要.pdf')
.result;
// 列表:按上传时间从新到旧
final page = await im.files.groupFiles(groupId, limit: 50);
debugPrint('共 ${page.fileCount} 个文件,${(page.usedBytes / 1024 / 1024).toStringAsFixed(1)} MB');
for (final f in page.items) {
debugPrint('${f.name} ${f.size} ${f.ownerNickname} ${f.createdAt}');
}
// 删除
await im.files.deleteGroupFile(groupId, info.fileId);- 每個群最多 1000 個群檔案。只有當前的群成員能檢視列表和下載,離開群之後不能再下載。
- 群檔案的變化沒有實時通知,開啟群檔案頁面時重新查詢。
- 群檔案的地址同樣要換取下載地址,或用
download()下載。
錄製語音
deeprespond_im_flutter 的 VoiceRecorder 用系統能力錄音(iOS 的 AVAudioRecorder、Android 的 MediaRecorder),直接錄成 AAC(M4A 容器,單聲道 64 kbps),各端都能播放,可以直接作為語音訊息傳送。錄音只支援 iOS 和 Android,其他平臺以 unsupported 拒絕。
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
// 用户按下按钮时
try {
await VoiceRecorder.start(); // 第一次使用时请求麦克风权限
} on DRException catch (e) {
if (e.code == LocalErrorCode.permissionRequired) {
showToast('请在系统设置中允许使用麦克风');
} else if (e.code == LocalErrorCode.deviceError) {
showToast('麦克风不可用');
}
return;
}
// 用户松开按钮时
final recording = await VoiceRecorder.stop();
await im.messages.send(SendTarget.user(peer), recording.toContent());
// 用户上滑取消时改为调用
// await VoiceRecorder.cancel();VoiceRecorder的方法都是靜態的,同時只能有一個錄音:正在錄音時再呼叫start()以invalid_state(reason為recording)拒絕,沒有在錄音時呼叫stop()以invalid_state(not_recording)拒絕。cancel()放棄錄音並刪除檔案,沒有在錄音時什麼也不做。- 錄音檔案預設寫在系統的臨時目錄中,可以用
start(directory: ...)指定目錄。傳送後 SDK 已在傳送佇列中儲存了自己的副本,錄音檔案可以由你刪除。 - 時長不足 1 秒的按 1 秒。錄音器不限制時長,語音檔案不能超過 5 MB(約 10 分鐘),請在介面上限制最長的錄音時間(如 60 秒)。
- 不用
toContent()時,也可以自己組織:OutgoingContent.voice(DRFile(recording.path, mimeType: 'audio/mp4'), duration: recording.duration)。
麥克風許可權
start() 在沒有許可權時先向使用者請求;使用者拒絕的以 permission_required 拒絕,details['permissions'] 為 ['microphone']。使用者選擇了“不再詢問”後系統不會再彈出請求,請引導使用者到系統設定中開啟。
- iOS:必須在
Info.plist中寫明使用麥克風的理由,否則 App 在請求許可權時會被系統終止:
<key>NSMicrophoneUsageDescription</key>
<string>用于录制语音消息</string>- Android:
RECORD_AUDIO許可權已在deeprespond_im_flutter的清單中宣告,構建時自動合併,不需要另外配置。
介面參考
im.files.upload()
上傳一個檔案。上傳前按上傳前的處理處理檔案。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
file | DRFile | 是 | 檔案 |
purpose | String | 是(命名引數) | 用途,見 UploadPurpose:attachment、user_avatar、group_avatar、chatroom_avatar、group_file |
kind | String? | 否(命名引數) | 型別,見 FileKind;預設按 mimeType 或內容判斷。三種頭像只能是 image,群檔案不能是 voice |
name | String? | 否(命名引數) | 檔名,預設為 DRFile 的名字 |
groupId | String? | 群檔案必填(命名引數) | 群 ID;其他用途不能帶 |
original | bool | 否(命名引數) | 圖片不壓縮,只去掉後設資料。預設 false |
onProgress | void Function(int loaded, int total)? | 否(命名引數) | 上傳進度 |
cancel | DRCancelToken? | 否(命名引數) | 用於取消 |
返回值:UploadTask,result 為 Future<FileInfo>。
可能的錯誤(result 以 DRException 失敗):
code | 說明 |
|---|---|
not_signed_in | 沒有登入 |
local_validation | reason:too_large 超出大小上限;invalid_purpose 用途與型別不匹配;required(field 為 group_id)群檔案缺少群 ID;invalid_value 其他用途帶了 groupId,或 purpose、kind 不認識,或檔案不存在;image_too_large、unsupported_type 不能按請求的型別上傳 |
unsupported | 部署沒有提供檔案服務(media_disabled) |
permission_denied | 應用關閉了群檔案(group_file_disabled) |
aborted | 已取消 |
limit_exceeded | 應用的儲存額度已滿(storage_quota),或群檔案數已滿 |
rate_limited | 上傳過於頻繁,需要等待的時間超過 60 秒(通常是當天的額度已用完) |
invalid_argument | 服務端檢查不透過,如 unsupported_type、invalid_image |
not_found、not_group_member | 群不存在,或本人不是群成員 |
storage_error | 讀寫本地檔案出錯,如臨時目錄空間不足 |
network_error、timeout | 自動重試後仍失敗 |
im.files.isFileUrl()
判斷一個地址是不是需要換取下載地址的檔案地址(包括縮圖地址)。同步執行。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
url | String | 是 | 地址 |
返回值:bool。部署沒有提供檔案服務、或還沒有登入(沒有執行配置)時一律為 false。
im.files.resolveUrl()
把一個檔案地址換成下載地址,結果按有效期快取。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
url | String | 是 | 檔案地址或縮圖地址 |
返回值:Future<DownloadUrl>。
可能的錯誤:file_expired、file_blocked、not_found、not_group_member,not_signed_in,以及網路錯誤。
im.files.resolveUrls()
把多個檔案地址換成下載地址,每項單獨給出結果。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
urls | List<String> | 是 | 檔案地址 |
返回值:Future<List<ResolvedUrl>>,按請求的順序。
im.files.download()
把檔案下載到本地。檔案地址先換取下載地址,其他地址直接請求(只支援 http、https)。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
url | String | 是 | 地址 |
savePath | String | 是 | 儲存的路徑,已有的檔案被覆蓋 |
onProgress | void Function(int loaded, int total)? | 否(命名引數) | 下載進度;服務端沒有給出大小時 total 為 0 |
cancel | DRCancelToken? | 否(命名引數) | 用於取消 |
返回值:Future<DownloadedFile>。
可能的錯誤:換取下載地址的錯誤;local_validation(savePath 為空,或地址不是 http、https);not_found(下載地址返回 404)、download_failed、internal(儲存服務返回 5xx)、network_error、aborted。
im.files.groupFiles()
查詢群檔案列表,按上傳時間從新到舊。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
cursor | String? | 否(命名引數) | 下一頁的遊標,即上一頁的 nextCursor |
limit | int? | 否(命名引數) | 每頁條數,預設 20,最大 100 |
返回值:Future<GroupFilesPage>。
可能的錯誤:not_found、not_group_member。
im.files.deleteGroupFile()
刪除一個群檔案:上傳者本人、群主或管理員。已刪除的同樣成功。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
groupId | String | 是 | 群 ID |
fileId | String | 是 | 檔案 ID |
返回值:Future<void>。
可能的錯誤:permission_denied(not_uploader:不是上傳者,也不是群主或管理員)、not_found、not_group_member、file_blocked(被遮蔽的檔案不能刪除)。
im.files.prepare()
按上傳前的規則處理檔案,不上傳。不需要登入。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
file | DRFile | 是 | 檔案 |
kind | String | 是 | 希望的型別,見 FileKind |
original | bool | 否(命名引數) | 圖片不壓縮,只去掉後設資料。預設 false |
workDir | String? | 否(命名引數) | 處理後的檔案寫在這個目錄中;預設在系統臨時目錄中新建一個 |
返回值:Future<PreparedFile>。
可能的錯誤:local_validation(kind 不認識,或檔案不存在)。
VoiceRecorder.start()
靜態方法。請求麥克風許可權(沒有時)並開始錄音。
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
directory | String? | 否(命名引數) | 錄音檔案的目錄,預設系統的臨時目錄 |
返回值:Future<void>。
可能的錯誤:unsupported(不是 iOS、Android,reason 為 platform)、invalid_state(recording:已在錄音)、permission_required(使用者沒有允許使用麥克風)、device_error(麥克風不可用,如被其他 App 佔用)。
VoiceRecorder.stop()
靜態方法。結束錄音,釋放麥克風。
返回值:Future<VoiceRecording>。
可能的錯誤:invalid_state(not_recording:沒有在錄音)、device_error。
VoiceRecorder.cancel()
靜態方法。放棄錄音並刪除錄音檔案;沒有在錄音時什麼也不做。
返回值:Future<void>。
VoiceRecorder.isRecording
靜態屬性,bool:是否正在錄音。
DRImageCache
DRImageCache.instance 是 DRImage 使用的磁碟快取。
| 方法 | 說明 |
|---|---|
fetch(DRClient client, String url, {onProgress}) | 返回 Future<File>:快取中有的直接返回,沒有的下載後返回。錯誤同 im.files.download() |
trim(int maxBytes) | 超過 maxBytes 時按最近使用淘汰到 80%。SDK 下載時會自動按 imageCacheBytes 呼叫 |
clear() | 清除全部快取 |
資料結構
DRFile
| 建構函式 / 欄位 | 型別 | 說明 |
|---|---|---|
DRFile(path, {mimeType, name}) | 本地檔案 | |
DRFile.fromBytes(bytes, {required name, mimeType}) | 記憶體中的內容 | |
path | String? | 檔案路徑 |
bytes | Uint8List? | 檔案內容 |
mimeType | String? | MIME 型別,用於判斷預設的型別 |
name | String? | 檔名 |
FileInfo
上傳完成後的檔案資訊。原始 JSON 在 raw 中。
| 欄位 | 型別 | 說明 |
|---|---|---|
fileId | String | 檔案 ID |
purpose | String | 用途 |
kind | String | 型別:image、voice、video、file |
status | String | 狀態,上傳完成後為 active |
url | String | 訊息附件和群檔案為檔案地址,頭像為公開地址 |
thumbnailUrl | String? | 縮圖地址,只有圖片型別的訊息附件和群檔案有 |
name | String? | 檔名 |
contentType | String | 服務端按內容識別出的格式,如 image/jpeg |
size | int | 大小,位元組 |
width | int? | 圖片和頭像的寬度,畫素 |
height | int? | 圖片和頭像的高度,畫素 |
owner | String? | 上傳者的使用者名稱 |
groupId | String? | 群檔案所在的群 ID |
via | String | 上傳方式,客戶端上傳的為 client |
createdAt | DateTime? | 上傳時間,保留期從這時算起 |
expiresAt | DateTime? | 過期時間:訊息附件和設定了保留天數的群檔案有值 |
GroupFile
群檔案,在 FileInfo 的欄位之外另有:
| 欄位 | 型別 | 說明 |
|---|---|---|
ownerNickname | String? | 上傳者的暱稱 |
ownerAvatarUrl | String? | 上傳者的頭像 |
GroupFilesPage
| 欄位 | 型別 | 說明 |
|---|---|---|
items | List<GroupFile> | 這一頁的群檔案 |
nextCursor | String? | 下一頁的遊標;為 null 表示沒有下一頁 |
fileCount | int | 這個群的群檔案總數 |
usedBytes | int | 這個群的群檔案佔用的位元組數 |
UploadTask
| 成員 | 型別 | 說明 |
|---|---|---|
result | Future<FileInfo> | 上傳的結果;取消後以 aborted 失敗 |
progress | Stream<UploadProgress> | 上傳進度(廣播流),上傳結束後關閉 |
cancel() | void | 取消上傳;已經結束的沒有影響 |
UploadProgress 有 loaded、total 兩個欄位(int,位元組)。
PreparedFile
| 欄位 | 型別 | 說明 |
|---|---|---|
path | String | 處理後要上傳的檔案;不需要改寫時為原檔案的路徑 |
kind | String | 建議的型別;不能按請求的型別處理時為 file |
name | String | 處理後的檔名;圖片重新編碼後副檔名隨之改變 |
format | String | 小寫的格式名,如 jpeg、png、mp4;file 為副檔名 |
contentType | String | MIME 型別 |
size | int | 處理後的大小,位元組 |
width | int? | 圖片、影片的寬度,讀不出時為 null |
height | int? | 圖片、影片的高度 |
durationSeconds | int? | 影片的時長,秒 |
thumbnailPath | String? | 影片第一幀的 JPEG 圖片;Windows、Linux 沒有 |
downgraded | String? | kind 改為 file 的原因:image_too_large 或 unsupported_type |
DownloadUrl
| 欄位 | 型別 | 說明 |
|---|---|---|
downloadUrl | String | 下載地址,直接請求,不帶任何登入憑證 |
expiresAt | DateTime | 到期時間(UTC) |
ResolvedUrl
| 欄位 | 型別 | 說明 |
|---|---|---|
url | String | 請求的檔案地址 |
downloadUrl | String? | 下載地址,失敗時為 null |
expiresAt | DateTime? | 下載地址的到期時間 |
error | DRException? | 失敗時的錯誤,見檔案地址與下載地址 |
DownloadedFile
| 欄位 | 型別 | 說明 |
|---|---|---|
path | String | 儲存的路徑 |
size | int | 寫入的位元組數 |
contentType | String? | 服務端返回的 Content-Type |
MediaOptions
DRClientOptions.media,建立客戶端時給出。
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
imageMaxSide | int | 2048 | 圖片壓縮後的最長邊,畫素 |
jpegQuality | int | 85 | 重新編碼 JPEG 的質量,1~100 |
imageCacheBytes | int | 200 MB | DRImage 磁碟快取的上限,位元組 |
OutboxOptions
DRClientOptions.outbox,建立客戶端時給出。
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
maxAge | Duration | 48 小時 | 未發出的訊息最多保留多久,超過的以 send_abandoned(expired)標為失敗 |
persistAttachmentMaxBytes | int | 200 MB | 不超過這個大小的附件複製到 SDK 的目錄中儲存,見隨訊息傳送的附件 |
VoiceRecording
| 成員 | 型別 | 說明 |
|---|---|---|
path | String | 錄音檔案(M4A) |
duration | Duration | 時長,至少 1 秒 |
toContent() | OutgoingContent | 作為語音訊息傳送的內容 |
