Tutorial 8 of 18

How to Send Structured Payloads

Send rich media and file attachments as typed data objects, with automatic chunking and complete payload delivery.

Images, Audio, Documents

14 min read

Send rich media and file attachments through send() using typed data objects. The SDK serializes your payload, chunks large content automatically, and delivers a complete message to onMessage.

data.typeTypical fieldsUse case
texttextChat messages
imagecontent (bytes)Photo sharing
audiocontent, durationMsVoice notes
doccontent, fileName, mimeTypeFile attachments

DNotifier does not enforce a fixed enum — define conventions in your app and handle them in onMessage.

Sending Flow

read file bytes locally
       │
       ▼
send({ data: { type: "image", content: bytes } })
       │
       ▼
SDK chunks if > messageSizeLimit
       │
       ▼
receiver onMessage → payload.toJSON() or raw()

Large files are split automatically. Your handler always receives a reassembled payload.

Receiving Structured Payloads

In onMessage, branch on body.type:

onMessage: (msg) => {
  const body = msg.payload.toJSON();
  if (body?.type === "image") {
    // body.content — image bytes
  } else if (body?.type === "audio") {
    // body.content, body.durationMs
  } else if (body?.type === "doc") {
    // body.content, body.fileName, body.mimeType
  }
},

JavaScript / TypeScript

Send images, audio, and documents via send() with typed data objects.

Image

import { readFileSync } from "fs";
import WebSocket from "ws";
import { DNotifier } from "@dnotifier-realtime/dnotifier";

const imageBytes = readFileSync("./photo.png");

const notifier = new DNotifier({
  appId: process.env.DNOTIFIER_APP_ID,
  secret: process.env.DNOTIFIER_SECRET,
  transport: "ws",
  userId: "user-alice",
  WebSocketImpl: WebSocket,
  onConnected: () => {},
  onMessage: (msg) => {
    const body = msg.payload.toJSON();
    if (body?.type === "image") {
      console.log("Received image, bytes:", body.content?.length);
    }
  },
  onDisconnected: () => {},
});

await notifier.connect();

await notifier.send({
  senderId: "user-alice",
  receiverId: "user-bob",
  data: {
    type: "image",
    content: imageBytes,
  },
});

Browser Image From File Input

const file = document.querySelector("input[type=file]").files[0];
const arrayBuffer = await file.arrayBuffer();
const imageBytes = new Uint8Array(arrayBuffer);

await notifier.send({
  senderId: myUserId,
  receiverId: peerUserId,
  data: {
    type: "image",
    content: imageBytes,
    fileName: file.name,
    mimeType: file.type,
  },
});

Audio

import { readFileSync } from "fs";

const audioBytes = readFileSync("./voice-note.m4a");

await notifier.send({
  senderId: "user-alice",
  receiverId: "user-bob",
  data: {
    type: "audio",
    content: audioBytes,
    durationMs: 12500,
    mimeType: "audio/mp4",
  },
});

Document

import { readFileSync } from "fs";

const docBytes = readFileSync("./report.pdf");

await notifier.send({
  senderId: "user-alice",
  receiverId: "user-bob",
  data: {
    type: "doc",
    content: docBytes,
    fileName: "report.pdf",
    mimeType: "application/pdf",
  },
});

Receiving and Displaying

onMessage: (msg) => {
  const body = msg.payload.toJSON();

  switch (body?.type) {
    case "image": {
      const blob = new Blob(
        [body.content],
        { type: body.mimeType ?? "image/png" },
      );
      const url = URL.createObjectURL(blob);
      showImagePreview(url);
      break;
    }
    case "audio":
      playAudioBuffer(body.content, body.mimeType);
      break;
    case "doc":
      offerDownload(body.content, body.fileName, body.mimeType);
      break;
  }
},

Size Limits

Check notifier.messageSizeLimit after connect(). Payloads larger than this limit are chunked automatically — you do not split files manually.

await notifier.connect();
console.log("Max chunk size:", notifier.messageSizeLimit);

Dart / Flutter

Send images, audio, and documents via send() with typed data objects.

Image

import 'dart:io';
import 'package:dnotifier/dnotifier.dart';

Future<void> sendImage(DNotifier notifier) async {
  final imageBytes = await File('path/to/photo.png').readAsBytes();

  await notifier.send(
    senderId: 'user-alice',
    receiverId: 'user-bob',
    data: {
      'type': 'image',
      'content': imageBytes,
    },
  );
}

Flutter Image Picker

import 'package:image_picker/image_picker.dart';

Future<void> sendPickedImage(DNotifier notifier, XFile file) async {
  final bytes = await file.readAsBytes();

  await notifier.send(
    senderId: myUserId,
    receiverId: peerUserId,
    data: {
      'type': 'image',
      'content': bytes,
      'fileName': file.name,
      'mimeType': 'image/jpeg',
    },
  );
}

Audio

import 'dart:io';

Future<void> sendAudio(DNotifier notifier) async {
  final audioBytes = await File('path/to/voice-note.m4a').readAsBytes();

  await notifier.send(
    senderId: 'user-alice',
    receiverId: 'user-bob',
    data: {
      'type': 'audio',
      'content': audioBytes,
      'durationMs': 12500,
      'mimeType': 'audio/mp4',
    },
  );
}

Document

import 'dart:io';

Future<void> sendDocument(DNotifier notifier) async {
  final docBytes = await File('path/to/report.pdf').readAsBytes();

  await notifier.send(
    senderId: 'user-alice',
    receiverId: 'user-bob',
    data: {
      'type': 'doc',
      'content': docBytes,
      'fileName': 'report.pdf',
      'mimeType': 'application/pdf',
    },
  );
}

Receiving Structured Payloads

onMessage: (msg) {
  final body = msg.payload.toJSON();
  if (body is! Map) return;

  switch (body['type']) {
    case 'image':
      final bytes = body['content'];
      if (bytes is List<int>) {
        showImagePreview(bytes);
      }
      break;
    case 'audio':
      playAudio(body['content'], body['mimeType'] as String?);
      break;
    case 'doc':
      saveAndOpenFile(
        body['content'],
        body['fileName'] as String?,
        body['mimeType'] as String?,
      );
      break;
  }
},

Size Limits

await notifier.connect();
print('Max chunk size: ${notifier.messageSizeLimit}');

Payloads larger than messageSizeLimit are chunked automatically.

Python

Send images, audio, and documents via send() with typed data objects.

Image

from pathlib import Path
from dnotifier import DNotifier

async def send_image(notifier: DNotifier):
    image_bytes = Path("path/to/photo.png").read_bytes()

    await notifier.send(
        sender_id="user-alice",
        receiver_id="user-bob",
        data={"type": "image", "content": image_bytes},
    )

With File Metadata

async def send_image_with_meta(notifier: DNotifier, file_path: str):
    image_bytes = Path(file_path).read_bytes()

    await notifier.send(
        sender_id=my_user_id,
        receiver_id=peer_user_id,
        data={
            "type": "image",
            "content": image_bytes,
            "fileName": Path(file_path).name,
            "mimeType": "image/jpeg",
        },
    )

Audio

from pathlib import Path

async def send_audio(notifier: DNotifier):
    audio_bytes = Path("path/to/voice-note.m4a").read_bytes()

    await notifier.send(
        sender_id="user-alice",
        receiver_id="user-bob",
        data={
            "type": "audio",
            "content": audio_bytes,
            "durationMs": 12500,
            "mimeType": "audio/mp4",
        },
    )

Document

from pathlib import Path

async def send_document(notifier: DNotifier):
    doc_bytes = Path("path/to/report.pdf").read_bytes()

    await notifier.send(
        sender_id="user-alice",
        receiver_id="user-bob",
        data={
            "type": "doc",
            "content": doc_bytes,
            "fileName": "report.pdf",
            "mimeType": "application/pdf",
        },
    )

Receiving Structured Payloads

def on_message(msg):
    body = msg.payload.to_json()
    if not isinstance(body, dict):
        return

    msg_type = body.get("type")
    if msg_type == "image":
        content = body.get("content")
        if isinstance(content, (bytes, bytearray)):
            show_image_preview(content)
    elif msg_type == "audio":
        play_audio(body.get("content"), body.get("mimeType"))
    elif msg_type == "doc":
        save_and_open_file(
            body.get("content"),
            body.get("fileName"),
            body.get("mimeType"),
        )

Size Limits

await notifier.connect()
print(f"Max chunk size: {notifier.message_size_limit}")

Payloads larger than message_size_limit are chunked automatically.