Files
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
2026-07-03 05:47:18 +02:00

7.4 KiB

05 — Medien & Dateien


Inhaltsverzeichnis


Datei hochladen

// Bytes hochladen → mxc:// URI zurück
final mxcUri = await client.uploadContent(
  fileBytes,                              // Uint8List
  filename: 'datei.pdf',
  contentType: 'application/pdf',         // optional, SDK erkennt es
);
print(mxcUri); // mxc://example.org/AbCdEf...

Bild senden

import 'package:matrix/matrix.dart';

// MatrixImageFile erstellen
final imageFile = MatrixImageFile(
  bytes: imageBytes,                      // Uint8List
  name: 'foto.jpg',
  mimeType: 'image/jpeg',
  width: 1920,                            // optional, für Info
  height: 1080,
);

// Mit Thumbnail
final thumbnail = MatrixImageFile(
  bytes: thumbnailBytes,
  name: 'foto_thumb.jpg',
  mimeType: 'image/jpeg',
  width: 320,
  height: 180,
);

// Senden (lädt automatisch hoch + sendet m.image Event)
await room.sendFileEvent(
  imageFile,
  thumbnail: thumbnail,                   // optional
);

// Alternativ mit sendImageEvent
await room.sendImageEvent(
  imageFile,
  thumbnail: thumbnail,
);

Video senden

final videoFile = MatrixVideoFile(
  bytes: videoBytes,
  name: 'video.mp4',
  mimeType: 'video/mp4',
  duration: 45000,                        // Millisekunden
  width: 1920,
  height: 1080,
);

final thumbFile = MatrixImageFile(
  bytes: thumbnailBytes,
  name: 'thumb.jpg',
  mimeType: 'image/jpeg',
  width: 320,
  height: 180,
);

await room.sendVideoEvent(
  videoFile,
  thumbnail: thumbFile,
);

Audio / Sprachnachricht senden

// Audio-Datei
final audioFile = MatrixAudioFile(
  bytes: audioBytes,
  name: 'aufnahme.ogg',
  mimeType: 'audio/ogg',
  duration: 12000,                        // Millisekunden
);

await room.sendAudioEvent(audioFile);

// Sprachnachricht (Voice Message MSC3245)
await room.sendFileEvent(
  audioFile,
  extraContent: {
    'org.matrix.msc3245.voice': {},       // markiert als Sprachnachricht
    'org.matrix.msc1767.audio': {
      'duration': 12000,
      'waveform': [0, 50, 100, 75, ...],  // Amplituden (0-1024)
    },
  },
);

Datei senden

final file = MatrixFile(
  bytes: fileBytes,
  name: 'dokument.pdf',
  mimeType: 'application/pdf',
);

await room.sendFileEvent(file);

// Mit Extra-Metadaten
await room.sendFileEvent(
  file,
  extraContent: {
    'org.matrix.msc3554.caption': {
      'msgtype': 'm.text',
      'body': 'Hier das Dokument',
    },
  },
);

Sticker senden

// Sticker sind ein eigener Event-Typ (m.sticker)
await room.sendEvent(
  {
    'body': 'Ein Sticker',
    'url': 'mxc://example.org/stickerId',
    'info': {
      'mimetype': 'image/png',
      'w': 256,
      'h': 256,
    },
  },
  EventTypes.Sticker,                     // 'm.sticker'
);

Medien herunterladen & entschlüsseln

// Aus Event-Attachment herunterladen (+ automatisch entschlüsseln bei E2EE)
final data = await event.downloadAndDecryptAttachment(
  getThumbnail: false,                    // true für Thumbnail statt Original
);
// data ist MatrixFile mit .bytes: Uint8List

// Direkte MXC-URL herunterladen
final bytes = await client.database.getFile(mxcUri);
// oder über HTTP:
final httpUri = mxcUri.getDownloadUri(client);
// → https://homeserver/_matrix/media/v3/download/server/mediaId

Thumbnails & Vorschaubilder

// Thumbnail-URL für Widget
final thumbnailUri = event.thumbnailUrl;  // mxc:// URI

// HTTP-URL für Thumbnail (mit Größe)
final httpUrl = thumbnailUri?.getThumbnail(
  client,
  width: 800,
  height: 600,
  method: ThumbnailMethod.scale,          // oder .crop
);

// Avatar-Thumbnail
final avatarUrl = user.avatarUrl?.getThumbnail(
  client,
  width: 56,
  height: 56,
  method: ThumbnailMethod.crop,
);

// Im Flutter Widget (CachedNetworkImage o.ä.):
Image.network(httpUrl.toString());

MXC-URL zu HTTP-URL

// Download-URL
final downloadUrl = Uri.parse(mxcUriString).getDownloadUri(client);
// → https://homeserver/_matrix/media/v3/download/server/mediaId

// Thumbnail-URL
final thumbUrl = Uri.parse(mxcUriString).getThumbnail(
  client,
  width: 300,
  height: 300,
  method: ThumbnailMethod.crop,
);

// Direkt als String
final urlString = downloadUrl.toString();

Verschlüsselte Medien

// In verschlüsselten Räumen werden Medien automatisch verschlüsselt.
// Das SDK übernimmt Ver- und Entschlüsselung transparent.

// Verschlüsselter Download (automatisch):
final file = await event.downloadAndDecryptAttachment();
// file.bytes enthält die entschlüsselten Rohdaten

// Manuell ver-/entschlüsseln (selten nötig):
// Nutze EncryptedFile aus dem matrix-package
final encryptedFile = EncryptedFile(
  url: 'mxc://...',
  key: {/* JWK */},
  iv: 'base64...',
  hashes: {'sha256': 'base64...'},
  v: 'v2',
);

Avatar hochladen

// Eigener Avatar
final avatarBytes = await File('avatar.png').readAsBytes();
final mxcUri = await client.uploadContent(
  avatarBytes,
  filename: 'avatar.png',
  contentType: 'image/png',
);
await client.setAvatarUrl(client.userID!, mxcUri);

// Raum-Avatar
final roomAvatarMxc = await client.uploadContent(imageBytes, filename: 'room.png');
await room.setAvatar(roomAvatarMxc);

MatrixFile-Objekte

// Basis-Klasse
MatrixFile({
  required Uint8List bytes,
  required String name,
  String? mimeType,
})

// Spezialisierte Unterklassen:
MatrixImageFile({
  required Uint8List bytes,
  required String name,
  String? mimeType,       // z.B. 'image/jpeg'
  int? width,
  int? height,
  bool? animated,         // für GIFs
})

MatrixVideoFile({
  required Uint8List bytes,
  required String name,
  String? mimeType,       // 'video/mp4'
  int? duration,          // ms
  int? width,
  int? height,
  MatrixImageFile? thumbnail,
})

MatrixAudioFile({
  required Uint8List bytes,
  required String name,
  String? mimeType,       // 'audio/ogg'
  int? duration,          // ms
})

// Eigenschaften
file.bytes              // Uint8List
file.name               // Dateiname
file.mimeType           // MIME-Typ
file.sizeString         // '1.4 MB' (formatiert)
file.size               // Bytes als int

Medien-Events lesen

// Attachment-Info aus Event
if (event.messageType == 'm.image') {
  final url = event.attachmentMxcUrl;     // mxc:// URI
  final mime = event.attachmentMimetype;  // 'image/jpeg'
  final size = event.attachmentSize;      // Bytes
  final width = event.infoMap['w'] as int?;
  final height = event.infoMap['h'] as int?;
}

if (event.messageType == 'm.video') {
  final duration = event.infoMap['duration'] as int?; // ms
}

if (event.messageType == 'm.audio') {
  final duration = event.infoMap['duration'] as int?;
  final waveform = event.content
      .tryGetMap('org.matrix.msc1767.audio')
      ?.tryGetList<int>('waveform');
}

// Datei-Größe als lesbarer String
print(event.sizeString); // '2.3 MB'