# Exporter des présentations au format HTML avec des images liées externes

> Exporter des présentations PowerPoint et OpenDocument au format HTML en Java en utilisant Aspose.Slides avec les images et autres ressources enregistrées comme fichiers liés externes.

Source: https://docs.aspose.com/slides/fr/java/exporting-presentations-to-html-with-externally-linked-images/
Updated: 2026-06-06

## **Vue d'ensemble**

Par défaut, Aspose.Slides exporte une présentation vers un fichier HTML autonome. Les images et autres ressources sont intégrées directement dans le HTML, généralement sous forme de données Base64. Cela est pratique lorsqu’on a besoin d’un seul fichier portable, mais ce n’est pas toujours le meilleur format pour un site Web, un CMS ou une chaîne de conversion côté serveur.

Utilisez des ressources liées externes lorsque vous souhaitez :

- réduire la taille du document HTML ;
- mettre en cache séparément les images, polices, audio ou vidéo dans le navigateur ou sur un CDN ;
- inspecter, remplacer, compresser ou post‑traiter les ressources générées après l’exportation ;
- conserver une structure de sortie plus proche de ce qu’attend une application Web.

Pour le flux de conversion HTML général, voir [Convertir des présentations PowerPoint en HTML](/slides/fr/java/convert-powerpoint-to-html/). Cet article se concentre sur la partie liaison des ressources de l’exportation.

## **Comment fonctionne l’exportation avec ressources liées**

[ILinkEmbedController](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) permet à votre application de décider, ressource par ressource, si l’exportateur intègre les données dans le HTML ou les enregistre à l’extérieur et écrit un lien.

L’interface possède trois méthodes :

- [ILinkEmbedController.getObjectStoringLocation](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) détermine si une ressource doit être liée ou intégrée.
- [ILinkEmbedController.getUrl](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) renvoie l’URL qui sera écrite dans le HTML généré ou vers une autre ressource liée.
- [ILinkEmbedController.saveExternal](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) écrit les données de la ressource liée sur le disque ou vers une autre cible de stockage.

Le chemin du système de fichiers et l’URL du navigateur sont des préoccupations distinctes. Par exemple, l’exemple ci‑dessous écrit les fichiers de ressources dans `html-output/assets` sur le disque, tandis que le HTML contient des URL relatives telles que `assets/resource-1.svg`. Un navigateur résout ces URL par rapport au fichier qui contient le lien. Ainsi, un lien depuis `presentation.html` vers un fichier SVG utilise `assets/resource-1.svg`, tandis qu’un lien depuis ce fichier SVG vers une image sauvegardée dans le même dossier `assets` utilise `resource-4.jpg`.

## **Exporter le HTML avec des ressources liées**

L’exemple Java suivant crée un répertoire de sortie, y enregistre le fichier HTML et stocke les ressources liées dans un sous‑dossier `assets`. Le contrôleur lie les ressources d’image, de police, audio, vidéo et CSS courantes lorsque Aspose.Slides les fournit ou peut déduire une extension de fichier sûre. Les ressources non reconnues restent intégrées.

```java
import com.aspose.slides.HtmlFormatter;
import com.aspose.slides.HtmlOptions;
import com.aspose.slides.ILinkEmbedController;
import com.aspose.slides.LinkEmbedDecision;
import com.aspose.slides.Presentation;
import com.aspose.slides.SVGOptions;
import com.aspose.slides.SaveFormat;
import com.aspose.slides.SlideImageFormat;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class ExportToHtmlWithLinkedResources {
    public static void main(String[] args) throws IOException {
        Path inputFilePath = Paths.get("presentation.pptx");
        Path outputDirectory = Paths.get("html-output");
        String assetDirectoryName = "assets";
        Path assetDirectory = outputDirectory.resolve(assetDirectoryName);

        Files.createDirectories(outputDirectory);
        Files.createDirectories(assetDirectory);

        String assetUrlPrefix = assetDirectoryName + "/";
        ExternalResourceController controller = new ExternalResourceController(assetDirectory, assetUrlPrefix);
        SVGOptions svgOptions = new SVGOptions(controller);
        SlideImageFormat slideImageFormat = SlideImageFormat.svg(svgOptions);

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

        Presentation presentation = new Presentation(inputFilePath.toString());
        try {
            Path htmlFilePath = outputDirectory.resolve("presentation.html");
            presentation.save(htmlFilePath.toString(), SaveFormat.Html, htmlOptions);
        } finally {
            presentation.dispose();
        }
    }

    private static final class ExternalResourceController implements ILinkEmbedController {
        private static final Map<String, String> EXTENSIONS_BY_CONTENT_TYPE = createExtensionsByContentType();

        private final Path assetDirectory;
        private final String assetUrlPrefix;
        private final Map<Integer, String> fileNamesByResourceId = new HashMap<>();

        private ExternalResourceController(Path assetDirectory, String assetUrlPrefix) {
            if (assetDirectory == null) {
                throw new IllegalArgumentException("The asset output directory must not be null.");
            }

            this.assetDirectory = assetDirectory;
            this.assetUrlPrefix = normalizeUrlPrefix(assetUrlPrefix);
        }

        @Override
        public int getObjectStoringLocation(
                int resourceId,
                byte[] entityData,
                String semanticName,
                String contentType,
                String recommendedExtension) {
            String extension = resolveExtension(contentType, recommendedExtension);
            if (extension == null) {
                return LinkEmbedDecision.Embed;
            }

            fileNamesByResourceId.put(resourceId, "resource-" + resourceId + extension);
            return LinkEmbedDecision.Link;
        }

        @Override
        public String getUrl(int resourceId, int referrer) {
            String fileName = fileNamesByResourceId.get(resourceId);
            if (fileName == null) {
                return null;
            }

            if (fileNamesByResourceId.containsKey(referrer)) {
                return fileName;
            }

            return assetUrlPrefix + fileName;
        }

        @Override
        public void saveExternal(int resourceId, byte[] entityData) {
            String fileName = fileNamesByResourceId.get(resourceId);
            if (fileName == null) {
                throw new IllegalStateException(
                        "Resource " + resourceId + " was not registered for external storage.");
            }

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

            try {
                Files.createDirectories(assetDirectory);
                Path filePath = assetDirectory.resolve(fileName);
                Files.write(filePath, entityData);
            } catch (IOException exception) {
                throw new IllegalStateException("Failed to save external resource " + resourceId + ".", exception);
            }
        }

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

        private static String resolveExtension(String contentType, String recommendedExtension) {
            if (contentType != null && !contentType.trim().isEmpty()) {
                String mappedExtension = EXTENSIONS_BY_CONTENT_TYPE.get(contentType);
                if (mappedExtension != null) {
                    return mappedExtension;
                }
            }

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

            return normalizeExtension(recommendedExtension);
        }

        private static boolean isSupportedContentType(String contentType) {
            return contentType != null &&
                    (contentType.regionMatches(true, 0, "image/", 0, "image/".length()) ||
                     contentType.regionMatches(true, 0, "font/", 0, "font/".length()) ||
                     contentType.regionMatches(true, 0, "audio/", 0, "audio/".length()) ||
                     contentType.regionMatches(true, 0, "video/", 0, "video/".length()));
        }

        private static String normalizeExtension(String extension) {
            if (extension == null || extension.trim().isEmpty()) {
                return null;
            }

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

            if (extensionCharacters.isEmpty()) {
                return null;
            }

            for (int index = 0; index < extensionCharacters.length(); index++) {
                char character = extensionCharacters.charAt(index);
                if (!Character.isLetterOrDigit(character)) {
                    return null;
                }
            }

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

        private static String normalizeUrlPrefix(String urlPrefix) {
            if (urlPrefix == null || urlPrefix.isEmpty()) {
                return "";
            }

            String normalizedUrlPrefix = urlPrefix.replace('\\', '/');
            return normalizedUrlPrefix.endsWith("/")
                    ? normalizedUrlPrefix
                    : normalizedUrlPrefix + "/";
        }
    }
}
```

Après l’exportation, le dossier de sortie possède cette structure :

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

Les fichiers exacts dépendent du contenu de la présentation et des options d’exportation. Par exemple, les images raster sont généralement exportées en JPEG ou PNG. Aspose.Slides peut choisir un codec d’image différent de celui utilisé dans la présentation source lorsqu’il produit un fichier plus petit ou plus approprié. Les images avec transparence sont exportées en PNG.

## **Choisir les URL pour le déploiement**

L’exemple utilise un préfixe d’URL relatif : `assets/`. Si `presentation.html` est ouvert depuis `html-output/presentation.html`, le navigateur charge `html-output/assets/resource-1.svg`.

Lorsque une ressource liée fait référence à une autre ressource liée, l’exemple utilise le paramètre `referrer` dans [ILinkEmbedController.getUrl](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) et ne renvoie que le nom de fichier. Par exemple, si `resource-1.svg` et `resource-4.jpg` se trouvent tous deux dans le dossier `assets`, le fichier SVG doit référencer `resource-4.jpg`, et non `assets/resource-4.jpg`.

Utilisez un préfixe d’URL différent lorsque les fichiers sont déployés ailleurs :

- Utilisez `assets/` lorsque le répertoire d’actifs se trouve à côté du fichier HTML.
- Utilisez `../assets/` lorsque le répertoire d’actifs est un niveau au-dessus du fichier HTML.
- Utilisez `https://cdn.example.com/presentations/job-123/assets/` lorsque les fichiers sont uploadés sur un CDN ou un serveur de fichiers statiques.

L’URL renvoyée par [ILinkEmbedController.getUrl](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/) doit correspondre à l’emplacement final déployé du fichier écrit par [ILinkEmbedController.saveExternal](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/). Dans les applications serveur, utilisez un répertoire de sortie unique ou un préfixe de stockage d’objet pour chaque travail de conversion afin d’éviter d’écraser les fichiers d’une autre exportation.

## **Quand intégrer plutôt que lier**

Le HTML intégré en Base64 reste utile lorsque la sortie doit être un seul fichier, par exemple une pièce jointe d’email, un aperçu hors ligne ou un document qui sera déplacé sans dossier d’actifs associé. Les ressources liées conviennent mieux lorsque le HTML sera servi par une application Web, stocké dans un CMS, optimisé par une chaîne de construction ou mis en cache par les navigateurs indépendamment du HTML.

## **FAQ**

**Puis‑je externaliser uniquement les images tout en conservant les autres ressources intégrées ?**

Oui. Dans [ILinkEmbedController.getObjectStoringLocation](https://reference.aspose.com/slides/fr/java/com.aspose.slides/ilinkembedcontroller/), renvoyez `LinkEmbedDecision.Link` uniquement pour les types de contenu que vous souhaitez sauvegarder sous forme de fichiers séparés, et renvoyez `LinkEmbedDecision.Embed` pour tout le reste.

**Pourquoi l’extension de l’image exportée diffère-t‑elle de celle de la présentation source ?**

Aspose.Slides peut ré‑encoder les images raster pendant l’exportation HTML afin d’améliorer la taille ou la compatibilité navigateur. Par exemple, une image du fichier source peut être écrite en JPEG ou PNG selon le résultat rendu.

**Les URL relatives fonctionnent‑elles après avoir déplacé le fichier HTML ?**

Les URL relatives ne fonctionnent que si la même structure de dossiers relative est conservée. Si le HTML référence `assets/resource-1.png`, le dossier `assets` doit rester à côté du fichier HTML sauf si vous générez un préfixe d’URL différent.

**Les applications serveur doivent‑elles réutiliser le même dossier de sortie ?**

Non. Utilisez un répertoire de sortie unique ou un préfixe de stockage pour chaque travail de conversion. Cela évite les collisions de noms de fichiers et empêche une exportation d’écraser les ressources générées par une autre exportation.
