Bilder fehlen beim Markdown-zu-Word-Export? So haben wir Cross-Origin- und WebP-Kompatibilitätsprobleme gelöst
Beim Aufbau der Markdown-zu-Word-Exportfunktion bei md-to.com stießen wir auf einen frustrierenden Fehler: Bilder im Markdown-Inhalt der Benutzer verschwanden nach dem Export in Word.
Die Ursache lag in zwei Schichten: Browser-Sicherheitsrichtlinie (CORS) und Bildformat-Kompatibilität (WebP). Dieser Beitrag dokumentiert den Debugging-Prozess und unsere endgültige Lösung.
Das Symptom
Ein Benutzer fügt Markdown wie dieses in den Editor ein:


Die Vorschau rendert beide Bilder einwandfrei. Aber in der exportierten Word-Datei fehlen beide Bilder — ersetzt durch Platzhaltertext wie [Editor-Oberfläche] und [Seitensymbol].
Problem 1: Word unterstützt kein WebP
Ursache
Wir verwenden die docx-Bibliothek zur Word-Dokumenterzeugung. Ihre ImageRun-Komponente unterstützt nur vier Bildformate:
type DocxImageType = 'jpg' | 'png' | 'gif' | 'bmp';
Moderne Websites verwenden intensiv WebP (25-35 % kleiner als PNG). Wenn unser Code auf ein nicht unterstütztes Format stieß, warf er einen Unsupported image type-Fehler und das Bild wurde stillschweigend verworfen.
Lösung: Automatische Konvertierung über Canvas
Browser rendern WebP nativ, daher können wir die Canvas-API verwenden, um es in PNG zu konvertieren:
function loadImageViaCanvas(
source: Blob | string,
): Promise<{ blob: Blob; width: number; height: number }> {
return new Promise((resolve, reject) => {
const img = new Image();
const isBlobSource = source instanceof Blob;
if (!isBlobSource) {
img.crossOrigin = 'anonymous';
}
const blobUrl = isBlobSource ? URL.createObjectURL(source) : null;
img.onload = () => {
if (blobUrl) URL.revokeObjectURL(blobUrl);
const { naturalWidth: width, naturalHeight: height } = img;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0);
canvas.toBlob(
(pngBlob) => {
if (pngBlob) resolve({ blob: pngBlob, width, height });
else reject(new Error('Canvas toBlob failed'));
},
'image/png',
);
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = blobUrl ?? (source as string);
});
}
Diese Funktion akzeptiert zwei Eingabetypen:
- Blob: für reine Formatkonvertierung bereits abgerufener Bilder
- URL-String: für CORS-Fallback (mehr dazu unten)
Unabhängig vom Eingabeformat (WebP, AVIF usw.) ist die Ausgabe immer PNG — was Word problemlos verarbeitet.
Problem 2: CORS blockiert Cross-Origin-Bildabrufe
Nach der Behebung des WebP-Problems entdeckten wir ein grundlegenderes Problem: Cross-Origin-Bilder konnten überhaupt nicht abgerufen werden.
Ursache
Unser Code verwendet fetch() zum Abrufen von Bilddaten:
const response = await fetch(imageUrl);
const blob = await response.blob();
Wenn die Bild-URL auf einer anderen Domain liegt als die Website (z.B. Website ist md-to.com, Bild ist auf example.com), blockiert die CORS (Cross-Origin Resource Sharing)-Richtlinie des Browsers die Anfrage:
Access to fetch at 'https://example.com/image.png'
from origin 'https://md-to.com' has been blocked by CORS policy
Warum funktioniert die Vorschau, aber nicht der Export?
Weil der Browser unterschiedliche Sicherheitsstufen für verschiedene Operationen anwendet:
| Operation | Cross-Origin erlaubt? | Warum |
|---|---|---|
<img src="extern"> Rendering | Ja | Browser rendert Bilder ohne CORS |
fetch() Datenzugriff | Blockiert | JS-Zugriff auf Binärdaten erfordert CORS |
<img crossOrigin> + Canvas Export | Blockiert | Kontaminiertes Canvas kann keine Daten exportieren |
fetch({mode: 'no-cors'}) | Unlesbar | Gibt leeren Body zurück |
Die Vorschau verwendet <img>-Tags (Cross-Origin-Rendering erlaubt), während der Export JavaScript benötigt, um die Binärdaten des Bildes zu lesen (durch CORS blockiert). Deshalb können Sie es „sehen, aber nicht exportieren”.
Warum Base64 auch nicht hilft
Man könnte denken: „Konvertiere die Bilder einfach in Base64!”
Aber Base64 ist nur ein Kodierungsformat, keine Datengewinnungsmethode. Das Problem ist nicht „in welchem Format speichern” — es ist „der Browser lässt den Zugriff auf die Rohdaten nicht zu.” Ob Sie in Base64, ArrayBuffer oder etwas anderes kodieren möchten — der erste Schritt ist das Abrufen der Daten — und CORS blockiert das vollständig.
Lösung: Serverseitiger Bildproxy
Da es keine browserseitige Umgehung gibt, lassen wir den Server die Bilder abrufen. Server haben keine CORS-Einschränkungen und können frei jede URL anfordern.
Wir haben eine leichtgewichtige Edge Function als Bildproxy erstellt:
Browser → /api/image-proxy?url=https://example.com/image.png → Unser Server → example.com
↑ Keine CORS-Einschränkung
Proxy-Funktion (am Edge deployed):
export async function onRequest({ request }) {
const { searchParams } = new URL(request.url);
const target = searchParams.get('url');
// Sicherheit: nur http(s), nur Bild-Typen, Größenlimit
const upstream = await fetch(target);
const contentType = upstream.headers.get('content-type');
return new Response(await upstream.arrayBuffer(), {
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=86400',
'Access-Control-Allow-Origin': '*',
},
});
}
Clientseitiges Auto-Routing:
function resolveImageSrc(src: string): string {
const resolved = new URL(src, window.location.href).toString();
// Cross-Origin-Bilder über Proxy leiten
if (new URL(resolved).origin !== window.location.origin) {
return `/api/image-proxy?url=${encodeURIComponent(resolved)}`;
}
return resolved;
}
Alle Cross-Origin-Bildanfragen werden nun zu Same-Origin-Anfragen — keine CORS-Probleme mehr.
Endgültige Architektur
Bild-URL im Markdown
│
▼
resolveImageSrc()
│
├── Same-Origin → Direkter Fetch
│
└── Cross-Origin → /api/image-proxy?url=xxx
│
▼
Edge Function leitet weiter
│
▼
Blob erhalten
│
▼
getDocxImageType() Format prüfen
│
┌─────────┴──────────┐
▼ ▼
jpg/png/gif/bmp webp/avif/andere
Direkt einbetten Canvas → PNG → Einbetten
Entwicklungsumgebung
Eine passende Proxy-Middleware wurde zum Vite-Entwicklungsserver hinzugefügt, um konsistentes Verhalten zwischen Entwicklung und Produktion sicherzustellen.
Sicherheitsmaßnahmen
Der Bildproxy ist kein uneingeschränkter offener Proxy:
- Erlaubt nur
http://- undhttps://-Protokolle - Leitet nur Bild-Content-Types weiter (png/jpeg/gif/bmp/webp/avif/svg)
- 10 MB Größenlimit pro Bild
- 24-Stunden-Cache auf Antworten
Zusammenfassung
| Problem | Ursache | Lösung |
|---|---|---|
| WebP-Bilder erscheinen nicht in Word | docx-Bibliothek unterstützt kein WebP | Canvas-API konvertiert in PNG |
| Cross-Origin-Bilder können nicht abgerufen werden | Browser-CORS-Sicherheitsrichtlinie | Serverseitige Edge-Function-Proxy |
| Bilder scheitern auch in der Entwicklungsumgebung | localhost → externe Domain ist Cross-Origin | Vite-Entwicklungsserver-Proxy-Middleware |
Kernaussage: Das Sicherheitsmodell des Browsers unterscheidet zwischen „Rendering” und „Datenzugriff”. Nur weil <img> ein Cross-Origin-Bild anzeigen kann, bedeutet das nicht, dass JavaScript seine Daten lesen kann. Wenn Sie clientseitigen Zugriff auf Cross-Origin-Ressourcen benötigen, ist ein serverseitiger Proxy praktisch der einzige zuverlässige Ansatz.
Möchten Sie es ausprobieren? Besuchen Sie md-to.com Markdown zu Word, fügen Sie Markdown mit externen Bildern ein und exportieren Sie.
Verwandte Tools
Markdown in Word (.docx) umwandeln – Online & Kostenlos
Markdown in Word-Dokumente umwandeln – kostenlos online. MD zu DOCX mit Echtzeit-Vorschau, Formatierung bleibt erhalten. Ohne Anmeldung, lokal im Browser.
Word in Markdown umwandeln – Kostenloser DOCX-zu-MD-Konverter
Word-Dokumente in Markdown umwandeln – kostenlos online. DOCX zu MD mit Erhaltung von Überschriften, Tabellen, Listen und Formatierung. Ohne Anmeldung.
Markdown in LaTeX umwandeln – Kostenloser Online-Konverter
Markdown in LaTeX umwandeln – kostenlos online. MD-Dokumente in TeX-Format für akademische Arbeiten und technische Dokumente transformieren. Ohne Anmeldung.