Plik PDF może zawierać inne pliki w środku. Może to być portfolio, które łączy umowy i arkusze kalkulacyjne, lub raport z dołączonymi danymi źródłowymi. Gdy wyciągasz te pliki w kodzie, chcesz, aby każdy został zapisany pod nazwą nadaną przez autora, a nie jako attachment1.bin. Ten samouczek pokazuje, jak wyodrębnić osadzone pliki z PDF‑a z zachowaniem oryginalnych nazw w C# przy użyciu Aspose.PDF for .NET. Otworzysz PDF, przejdziesz przez jego osadzone pliki, ustalisz nazwę każdego pliku za pomocą FileSpecification.GetFileName i zapiszesz zawartość do folderu.

Gdzie PDF przechowuje nazwę osadzonego pliku

Każdy osadzony plik jest opisany przez specyfikację pliku, a specyfikacja pliku może zapisywać nazwę w więcej niż jednym miejscu. Istnieje nazwa pliku Unicode, standardowa nazwa pliku oraz klucz, pod którym plik jest wymieniony w kolekcji osadzonych plików dokumentu. Producenci PDF nie wypełniają tych pól konsekwentnie, a niektórzy całkowicie pomijają nazwę.

Aspose.PDF for .NET udostępnia właściwość FileSpecification.GetFileName, aby sobie z tym poradzić. Rozwiązuje nazwę z nazwy Unicode, zwykłej nazwy pliku lub klucza kolekcji, w zależności od tego, co jest dostępne, więc nadal otrzymujesz użyteczną nazwę, gdy nazwa Unicode jest nieobecna. Możesz przekazać nazwę awaryjną dla plików, które nie mają żadnej nazwy. Opcjonalny drugi parametr kontroluje, czy dopuszczalna jest pusta nazwa, gdy nie ma dostępnej nazwy pliku. Otrzymujesz jedno wywołanie zamiast własnej logiki odczytywania słowników PDF.

Uwaga: Przykłady w tym artykule używają wyłącznie argumentu awaryjnego.

Document.EmbeddedFiles przechowuje załączniki na poziomie dokumentu, które obejmują pliki w portfolio PDF. Pliki dołączone do konkretnej strony za pomocą adnotacji załącznika pliku (zwykle wyświetlane jako ikona spinacza) są przechowywane w samej adnotacji; Wyodrębnij pliki dołączone do stron opisuje te przypadki.

Używanie Aspose.PDF do wyodrębniania osadzonych plików z pliku PDF

Install-Package Aspose.PDF

Lub przy użyciu .NET CLI:

dotnet add package Aspose.PDF
  • PDF zawierający osadzone pliki. Przykład używa portfolio o nazwie PDFPortfolio.pdf.

Bez licencji Aspose.PDF działa w trybie ewaluacyjnym. Aby przetestować bez ograniczeń ewaluacyjnych, poproś o bezpłatną tymczasową licencję.

Wyodrębnij każdy osadzony plik z jego oryginalną nazwą

Poniższa metoda otwiera plik PDF, zapisuje każdy osadzony plik do folderu wyjściowego pod jego rozpoznaną nazwą i wypisuje ścieżkę każdego zapisanego pliku.

using System;
using System.IO;
using Aspose.Pdf;

public static class EmbeddedFileExtractor
{
    public static void ExtractEmbeddedFiles(string inputPdf, string outputDir)
    {
        Directory.CreateDirectory(outputDir);

using (var document = new Document(inputPdf))
        {
            foreach (FileSpecification fileSpec in document.EmbeddedFiles)
            {
                // Resolve the stored name, then keep only the file name part.
                string fileName = Path.GetFileName(fileSpec.GetFileName("attachment.bin"));
                string outputPath = Path.Combine(outputDir, fileName);

using (var output = new FileStream(outputPath, FileMode.Create))
                {
                    fileSpec.Contents.CopyTo(output);
                }

Console.WriteLine($"Saved: {outputPath}");
            }
        }
    }
}

Wywołaj go, podając ścieżkę do pliku PDF oraz folder, w którym mają zostać zapisane pliki:

EmbeddedFileExtractor.ExtractEmbeddedFiles("PDFPortfolio.pdf", "ExtractedFiles");

Co robi każdy krok

Otwórz dokument. new Document(inputPdf) ładuje plik PDF. Blok using zwalnia uchwyt pliku po zakończeniu przetwarzania, nawet jeśli zostanie rzucony wyjątek.

Iteruj po załącznikach. document.EmbeddedFiles zwraca jeden FileSpecification dla każdego pliku osadzonego na poziomie dokumentu.

Rozwiąż nazwę. fileSpec.GetFileName("attachment.bin") zwraca zapisaną nazwę, lub attachment.bin, jeśli PDF nie dostarcza żadnej. Wybierz alternatywę, która ma sens w Twojej aplikacji.

Usuwaj segmenty folderów. Przechowywana nazwa może zawierać ścieżkę, taką jak reports/2026/q3.xlsx, lub sfałszowaną wartość, taką jak ..\..\startup.bat. Przekazanie wyniku przez Path.GetFileName skraca go do q3.xlsx lub startup.bat, więc plik nie może zostać zapisany poza outputDir. Zachowaj ten krok, nawet jeśli ufasz PDF‑om, które przetwarzasz dzisiaj.

Zapisz zawartość. fileSpec.Contents jest strumieniem danych osadzonego pliku. CopyTo zapisuje go do nowego pliku bez żadnej konwersji.

Oczekiwany wynik

Folder wyjściowy zawiera jeden plik dla każdego załącznika na poziomie dokumentu, nazwany tak, jak został zapisany w pliku PDF. Dla portfolio, które zawiera arkusz kalkulacyjny i dokument Word, wyjście konsoli wygląda podobnie do tego:

Saved: ExtractedFiles\budget-2026.xlsx
Saved: ExtractedFiles\service-agreement.docx

Nazwy plików zależą od Twojego pliku PDF. Wyodrębnione pliki zawierają te same dane, które zostały osadzone, więc każdy z nich otwiera się w aplikacji obsługującej jego format.

Obsługa duplikatów i niebezpiecznych nazw

Podstawowy przykład ma trzy słabe punkty, z którymi spotkasz się w rzeczywistych plikach PDF:

  • Dwa załączniki mogą mieć tę samą nazwę, a FileMode.Create cicho nadpisuje pierwszy. Dzieje się to także, gdy kilka załączników nie ma nazwy i wszystkie domyślnie używają attachment.bin.
  • Nazwa, która jest prawidłowa w jednym systemie operacyjnym, może zawierać znaki nieprawidłowe w innym, np. : w systemie Windows.
  • Przechowywana nazwa kończąca się separatorem ścieżki powoduje, że Path.GetFileName zwraca pusty ciąg.

Ten pomocnik obsługuje wszystkie trzy. Zastępuje nieprawidłowe znaki, używa wartości domyślnej, gdy nazwa jest pusta, i dodaje licznik, gdy plik już istnieje:

private static string GetSafeOutputPath(string outputDir, string resolvedName, string fallbackName)
{
    string fileName = Path.GetFileName(resolvedName);

foreach (char c in Path.GetInvalidFileNameChars())
    {
        fileName = fileName.Replace(c, '_');
    }

if (string.IsNullOrWhiteSpace(fileName))
    {
        fileName = fallbackName;
    }

string baseName = Path.GetFileNameWithoutExtension(fileName);
    string extension = Path.GetExtension(fileName);
    string outputPath = Path.Combine(outputDir, fileName);

int counter = 1;
    while (File.Exists(outputPath))
    {
        outputPath = Path.Combine(outputDir, $"{baseName} ({counter}){extension}");
        counter++;
    }

return outputPath;
}

W pętli ekstrakcji zamień dwie linie, które tworzą fileName i outputPath, na:

string outputPath = GetSafeOutputPath(outputDir, fileSpec.GetFileName("attachment.bin"), "attachment.bin");

Ponieważ pomocnik sprawdza istniejące pliki, uruchomienie ekstrakcji dwa razy do tego samego folderu powoduje tworzenie numerowanych kopii zamiast nadpisywania. Wyczyść folder najpierw, jeśli chcesz uzyskać świeży wynik przy każdym uruchomieniu.

Wyodrębnij pliki dołączone do stron

Jeśli EmbeddedFiles jest pusty, ale w przeglądarce PDF widzisz ikonę spinacza, plik prawdopodobnie został dołączony za pomocą adnotacji załącznika pliku na stronie. Adnotacja przechowuje własny FileSpecification w właściwości File, więc stosuje się ta sama rozdzielczość nazw i ten sam pomocnik. Ta metoda sprawdza każdą stronę i zapisuje każdy dołączony plik do folderu wyjściowego:

using Aspose.Pdf.Annotations;

public static void ExtractPageAttachments(string inputPdf, string outputDir)
{
    Directory.CreateDirectory(outputDir);

using (var document = new Document(inputPdf))
    {
        foreach (Page page in document.Pages)
        {
            foreach (Annotation annotation in page.Annotations)
            {
                if (annotation is FileAttachmentAnnotation attachment && attachment.File != null)
                {
                    string outputPath = GetSafeOutputPath(outputDir, attachment.File.GetFileName("attachment.bin"), "attachment.bin");

using (var output = new FileStream(outputPath, FileMode.Create))
                    {
                        attachment.File.Contents.CopyTo(output);
                    }

Console.WriteLine($"Saved: {outputPath}");
                }
            }
        }
    }
}

Aby zebrać wszystkie załączniki w pliku PDF, uruchom obie metody z tym samym folderem wyjściowym. Jeśli PDF odwołuje się do tego samego pliku z obu miejsc, GetSafeOutputPath zapisuje drugą kopię pod nazwą z numerem zamiast nadpisywać pierwszą.

Kolejne kroki

Aby dowiedzieć się więcej o załącznikach, takich jak dodawanie, usuwanie lub wyświetlanie ich listy bez wyodrębniania, zobacz dokumentację Aspose.PDF for .NET. FileSpecification odniesienie klasy wymienia właściwości other name i description, które możesz odczytać z każdego osadzonego pliku. Jeśli PDF nie zostanie wyodrębniony zgodnie z oczekiwaniami, zamieść przykład na forum wsparcia Aspose.PDF.

Najczęściej zadawane pytania

  1. Jak metoda FileSpecification.GetFileName wybiera nazwę?
    Rozwiązuje nazwę z nazwy Unicode osadzonego pliku, zwykłej nazwy pliku lub klucza kolekcji, w zależności od tego, co jest dostępne. Jeśli nic nie jest dostępne, zwraca nazwę awaryjną podaną jako parametr. Metoda została dodana w Aspose.PDF for .NET 26.9.

  2. Czy to działa dla plików PDF, które nie są portfolio? Tak, dla plików dołączonych na poziomie dokumentu. Pliki dołączone do konkretnej strony za pomocą adnotacji załącznika pliku (zwykle wyświetlanej jako ikona spinacza) są przechowywane w samej adnotacji, więc należy odczytać je z FileAttachmentAnnotation.File, jak pokazano w Wyodrębnij pliki dołączone do stron.

  3. Dlaczego opakować wynik GetFileName w Path.GetFileName? Nazwa przechowywana w pliku PDF jest ustawiana przez osobę, która utworzyła plik i może zawierać segmenty folderów. Path.GetFileName zachowuje tylko nazwę pliku, więc każdy załącznik jest zapisywany w Twoim folderze wyjściowym.

  4. Co się stanie, jeśli dwa załączniki mają taką samą nazwę? With FileMode.Create, the second file overwrites the first. Check whether the path already exists and add a numeric suffix, as shown in Handle duplicate and unsafe names.

  5. Czy mogę przetworzyć załącznik bez zapisywania go na dysku? Tak. Właściwość Contents jest typu Stream, więc możesz skopiować ją do MemoryStream, przesłać ją lub przekazać do innej biblioteki zamiast zapisywać do FileStream.