Prezentációk exportálása HTML-be külsőleg linkelt képekkel

Áttekintés

Alapértelmezés szerint az Aspose.Slides egy prezentációt önálló HTML fájlba exportál. A képek és egyéb erőforrások közvetlenül az HTML-be íródnak, általában Base64 adatként. Ez akkor kényelmes, ha egy hordozható fájlra van szükség, de nem mindig a legjobb formátum egy weboldal, egy CMS vagy egy szerveroldali konverziós folyamat számára.

Használjon külsőleg hivatkozott erőforrásokat, ha:

  • csökkentse a HTML dokumentum méretét;
  • gyorsítótárazza a képeket, betűtípusokat, hangot vagy videót külön a böngészőben vagy CDN-ben;
  • ellenőrizze, cserélje, tömörítse vagy utófeldolgozza a generált erőforrásokat exportálás után;
  • tartsa a kimeneti struktúrát közelebb ahhoz, amit egy webalkalmazás elvár.

Az általános HTML konverziós munkafolyamatnál lásd a PowerPoint prezentációk konvertálása HTML-re. Ez a cikk az export erőforrás‑linkelés részére koncentrál.

A linkelt erőforrás exportálás működése

A ILinkEmbedController Java proxyja lehetővé teszi, hogy az alkalmazása erőforrásonként eldöntse, hogy az exportáló beágyazza-e az adatot a HTML-be vagy külsőleg menti és linket ír.

A vezérlőnek három metódusa van:

A fájlrendszer útvonala és a böngésző URL-je külön kérdés. Például az alábbi minta az erőforrás‑fájlokat a html-output/assets könyvtárba írja lemezre, míg a HTML relatív URL‑eket tartalmaz, mint assets/resource-1.svg. A böngésző ezeket a URL‑eket a linket tartalmazó fájlhoz viszonyítva oldja fel. Így egy link a presentation.html‑ből egy SVG fájlra a assets/resource-1.svg‑t használja, míg az SVG‑fájl egy ugyanabban a assets könyvtárban lévő képre a resource-4.jpg‑t hivatkozza.

HTML exportálása linkelt erőforrásokkal

Az alábbi JavaScript‑példa létrehoz egy kimeneti könyvtárat, oda menti a HTML‑fájlt, és a linkelt erőforrásokat egy assets alkönyvtárban tárolja. A vezérlő a gyakori kép‑, betűtípus‑, hang‑, videó‑ és CSS‑erőforrásokat linkeli, ha az Aspose.Slides biztosít vagy meg tud határozni biztonságos fájlkiterjesztést. A nem felismert erőforrások beágyazva maradnak.

var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const fs = require("fs");
const path = require("path");

class ExternalResourceController {
    constructor(assetDirectory, assetUrlPrefix) {
        if (assetDirectory == null || assetDirectory.trim().length === 0) {
            throw new Error("The asset output directory must not be empty.");
        }

        this.assetDirectory = assetDirectory;
        this.assetUrlPrefix = normalizeUrlPrefix(assetUrlPrefix);
        this.fileNamesByResourceId = new Map();
    }

    createProxy() {
        const linkEmbedControllerInterfaceName = "com.aspose.slides.ILinkEmbedController";
        let controller = this;
        return java.newProxy(linkEmbedControllerInterfaceName, {
            getObjectStoringLocation: function(resourceId, entityData, semanticName, contentType, recommendedExtension) {
                return controller.getObjectStoringLocation(
                    resourceId,
                    entityData,
                    semanticName,
                    contentType,
                    recommendedExtension);
            },
            getUrl: function(resourceId, referrer) {
                return controller.getUrl(resourceId, referrer);
            },
            saveExternal: function(resourceId, entityData) {
                controller.saveExternal(resourceId, entityData);
            }
        });
    }

    getObjectStoringLocation(resourceId, entityData, semanticName, contentType, recommendedExtension) {
        let extension = resolveExtension(contentType, recommendedExtension);
        if (extension == null) {
            return aspose.slides.LinkEmbedDecision.Embed;
        }

        this.fileNamesByResourceId.set(resourceId, "resource-" + resourceId + extension);
        return aspose.slides.LinkEmbedDecision.Link;
    }

    getUrl(resourceId, referrer) {
        let fileName = this.fileNamesByResourceId.get(resourceId);
        if (fileName == null) {
            return null;
        }

        if (this.fileNamesByResourceId.has(referrer)) {
            return fileName;
        }

        return this.assetUrlPrefix + fileName;
    }

    saveExternal(resourceId, entityData) {
        let fileName = this.fileNamesByResourceId.get(resourceId);
        if (fileName == null) {
            throw new Error("Resource " + resourceId + " was not registered for external storage.");
        }

        if (entityData == null || entityData.length === 0) {
            throw new Error("Resource " + resourceId + " contains no data and cannot be saved.");
        }

        fs.mkdirSync(this.assetDirectory, { recursive: true });

        let filePath = path.join(this.assetDirectory, fileName);
        let fileData = Buffer.from(entityData);
        fs.writeFileSync(filePath, fileData);
    }
}

function createExtensionsByContentType() {
    let extensionsByContentType = new Map();
    extensionsByContentType.set("image/jpeg", ".jpg");
    extensionsByContentType.set("image/png", ".png");
    extensionsByContentType.set("image/gif", ".gif");
    extensionsByContentType.set("image/bmp", ".bmp");
    extensionsByContentType.set("image/svg+xml", ".svg");
    extensionsByContentType.set("image/tiff", ".tiff");
    extensionsByContentType.set("image/x-emf", ".emf");
    extensionsByContentType.set("image/x-wmf", ".wmf");
    extensionsByContentType.set("font/woff", ".woff");
    extensionsByContentType.set("font/woff2", ".woff2");
    extensionsByContentType.set("font/ttf", ".ttf");
    extensionsByContentType.set("application/font-woff", ".woff");
    extensionsByContentType.set("application/vnd.ms-fontobject", ".eot");
    extensionsByContentType.set("application/x-font-ttf", ".ttf");
    extensionsByContentType.set("text/css", ".css");
    extensionsByContentType.set("audio/mpeg", ".mp3");
    extensionsByContentType.set("audio/mp4", ".m4a");
    extensionsByContentType.set("audio/wav", ".wav");
    extensionsByContentType.set("video/mp4", ".mp4");
    extensionsByContentType.set("video/webm", ".webm");
    return extensionsByContentType;
}

let extensionsByContentType = createExtensionsByContentType();

function resolveExtension(contentType, recommendedExtension) {
    if (contentType != null && contentType.trim().length > 0) {
        let mappedExtension = extensionsByContentType.get(contentType);
        if (mappedExtension != null) {
            return mappedExtension;
        }
    }

    if (!isSupportedContentType(contentType)) {
        return null;
    }

    return normalizeExtension(recommendedExtension);
}

function isSupportedContentType(contentType) {
    if (contentType == null) {
        return false;
    }

    let normalizedContentType = contentType.toLowerCase();
    return normalizedContentType.startsWith("image/") ||
        normalizedContentType.startsWith("font/") ||
        normalizedContentType.startsWith("audio/") ||
        normalizedContentType.startsWith("video/");
}

function normalizeExtension(extension) {
    if (extension == null || extension.trim().length === 0) {
        return null;
    }

    let extensionCharacters = extension.trim();
    while (extensionCharacters.startsWith(".")) {
        extensionCharacters = extensionCharacters.substring(1);
    }

    if (extensionCharacters.length === 0) {
        return null;
    }

    for (let index = 0; index < extensionCharacters.length; index++) {
        let character = extensionCharacters[index];
        if (!/[A-Za-z0-9]/.test(character)) {
            return null;
        }
    }

    return "." + extensionCharacters.toLowerCase();
}

function normalizeUrlPrefix(urlPrefix) {
    if (urlPrefix == null || urlPrefix.length === 0) {
        return "";
    }

    let normalizedUrlPrefix = urlPrefix.replace(/\\/g, "/");
    return normalizedUrlPrefix.endsWith("/")
        ? normalizedUrlPrefix
        : normalizedUrlPrefix + "/";
}

let inputFilePath = "presentation.pptx";
let outputDirectory = "html-output";
let assetDirectoryName = "assets";
let assetDirectory = path.join(outputDirectory, assetDirectoryName);

fs.mkdirSync(outputDirectory, { recursive: true });
fs.mkdirSync(assetDirectory, { recursive: true });

let assetUrlPrefix = assetDirectoryName + "/";
let controllerWrapper = new ExternalResourceController(assetDirectory, assetUrlPrefix);
let controller = controllerWrapper.createProxy();
let svgOptions = new aspose.slides.SVGOptions(controller);
let slideImageFormat = aspose.slides.SlideImageFormat.svg(svgOptions);

let htmlOptions = new aspose.slides.HtmlOptions(controller);
htmlOptions.setHtmlFormatter(aspose.slides.HtmlFormatter.createDocumentFormatter("", false));
htmlOptions.setSlideImageFormat(slideImageFormat);

let presentation = new aspose.slides.Presentation(inputFilePath);
try {
    let htmlFilePath = path.join(outputDirectory, "presentation.html");
    presentation.save(htmlFilePath, aspose.slides.SaveFormat.Html, htmlOptions);
} finally {
    if (presentation != null) {
        presentation.dispose();
    }
}

Az export után a kimeneti mappának ez a struktúrája van:

html-output/
  presentation.html
  assets/
    resource-1.svg
    resource-2.svg
    resource-3.svg
    resource-4.jpg
    resource-5.png

A pontos fájlok a prezentáció tartalmától és az exportálási beállításoktól függnek. Például a raszteres képek általában JPEG‑ként vagy PNG‑ként kerülnek exportálásra. Az Aspose.Slides másik képkódolót is választhat, mint a forrásprezentációban, ha ez kisebb vagy megfelelőbb fájlt eredményez. Az átlátszóságot tartalmazó képek PNG‑ként kerülnek exportálásra.

URL-ek kiválasztása a telepítéshez

A minta egy relatív URL‑előtagot használ: assets/. Ha a presentation.html a html-output/presentation.html‑ból nyílik, a böngésző a html-output/assets/resource-1.svg‑t tölti be.

Amikor egy linkelt erőforrás egy másik linkelt erőforrásra hivatkozik, a minta a referrer paramétert használja az ILinkEmbedController.getUrl‑ban, és csak a fájlnevet adja vissza. Például ha a resource-1.svg és a resource-4.jpg is a assets mappában van, az SVG‑fájlnak a resource-4.jpg‑re kell hivatkoznia, nem a assets/resource-4.jpg‑re.

Használjon más URL‑előtagot, ha a fájlok máshol vannak telepítve:

  • Használja a assets/‑t, ha az eszközkönyvtár a HTML‑fájl mellett helyezkedik el.
  • Használja a ../assets/‑t, ha az eszközkönyvtár a HTML‑fájl egy szinttel feljebb van.
  • Használja a https://cdn.example.com/presentations/job-123/assets/‑t, ha a fájlok egy CDN‑re vagy statikus fájlszerverre vannak feltöltve.

Az ILinkEmbedController.getUrl által visszaadott URL‑nek meg kell egyeznie a ILinkEmbedController.saveExternal által írt fájl végső telepítési helyével. Szerveralkalmazásoknál használjon egyedi kimeneti könyvtárat vagy objektumtároló előtagot minden konverziós feladathoz, hogy elkerülje a fájlok felülírását másik exportból.

Mikor érdemes beágyazni

A beágyazott Base64 HTML továbbra is hasznos, ha a kimenetnek egyetlen fájlnak kell lennie, például e‑mail‑mellékletként, offline előnézetként vagy egy olyan dokumentumként, amelyet támogatás nélküli eszközkönyvtár nélkül mozgatnak. A linkelt erőforrások jobb megoldást nyújtanak, ha a HTML‑t egy webalkalmazás szolgálja ki, egy CMS‑ben tárolják, egy build‑csővezeték optimalizálja, vagy a böngészők a HTML‑től függetlenül gyorsítótárazzák.

GYIK

Kiexportálhatok csak képeket, és a többi erőforrást beágyazottan hagyhatom?

Igen. Az ILinkEmbedController.getObjectStoringLocation csak a külön fájlokként menteni kívánt tartalomtípusok esetén adja vissza a LinkEmbedDecision.Link értéket, egyébként a LinkEmbedDecision.Embed értéket.

Miért tér el az exportált kép kiterjesztése a forrásprezentációétól?

Az Aspose.Slides a HTML exportálás során újrakódolhatja a raszteres képeket a méret vagy a böngésző kompatibilitás javítása érdekében. Például a forrásfájlból származó kép JPEG‑ként vagy PNG‑ként íródhat, a megjelenített eredménytől függően.

Működnek a relatív URL‑ek, ha áthelyezem a HTML‑fájlt?

A relatív URL‑ek csak akkor működnek, ha a relatív mappaszerkezet változatlanul megmarad. Ha a HTML a assets/resource-1.png‑re hivatkozik, az assets mappának a HTML‑fájl mellett kell maradnia, hacsak nem generál egy másik URL‑előtagot.

A szerveralkalmazások újra felhasználhatják ugyanazt a kimeneti mappát?

Nem. Használjon egyedi kimeneti könyvtárat vagy tárolási előtagot minden konverziós feladathoz. Így elkerülhető a fájlnév-ütközés, és egy export nem ír felül egy másik export által generált erőforrásokat.