Вопрос или проблема
Я сталкиваюсь с проблемами при попытке сохранить многостраничное изображение TIFF в MemoryStream. Я видел множество материалов о том, как сохранить многостраничный TIFF, но все примеры сохраняют изображение в файл, а не в MemoryStream.
Когда я сохраняю в файл, все работает, как и ожидалось (я получаю три страницы в изображении TIFF на диске). Однако когда я пытаюсь сохранить в MemoryStream, а затем взять этот MemoryStream и сохранить в файл, я получаю только первую страницу.
Вот что у меня сейчас, и в итоге я получаю только первую страницу.
public void Test()
{
// Добавить 3 файла изображений в кеш
List<Image> imageCache = new List<Image>();
imageCache.Add(Image.FromFile(@"c:\test\testInput1.tif"));
imageCache.Add(Image.FromFile(@"c:\test\testInput2.tif"));
imageCache.Add(Image.FromFile(@"c:\test\testInput3.tif"));
// Получить кодек изображения TIFF
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType == "image/tiff").First();
if (codec == null)
throw new Exception("Кодек TIFF не найден.");
// Создать параметр кодировщика сжатия
EncoderParameter compressionParam = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
// Создать параметр для первого кадра страницы
EncoderParameter firstFrameParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
// Создать параметры для дополнительных страниц
EncoderParameter additionalFramesParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
// Создать параметр цветовой глубины
EncoderParameter colorDepthParam = new EncoderParameter(Encoder.ColorDepth, (long)1);
// Создать параметр сброса
EncoderParameter flushParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
// Создать параметр последнего кадра
EncoderParameter lastPageParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.LastFrame);
// Создать параметры кодировщика для первой страницы
EncoderParameters firstFrameParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
firstFrameParam,
}
};
// Создать параметры кодировщика для дополнительных страниц
EncoderParameters additionalFrameParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
additionalFramesParam,
}
};
// Создать параметры кодировщика для сохранения в файл
EncoderParameters saveToFileParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
colorDepthParam,
}
};
// Создать параметры сброса
EncoderParameters flushParams = new EncoderParameters(1)
{
Param = new EncoderParameter[]
{
flushParam,
lastPageParam,
}
};
// Сохранить битмап в память как TIFF
using (MemoryStream byteStream = new MemoryStream())
{
_imageCache[0].Save(byteStream, ImageFormat.Tiff);
// Поместить TIFF в другой объект Image
using (Image tiff = Image.FromStream(byteStream))
{
// Сохранить в файл - раскомментируйте следующую строку для работающего решения
//tiff.Save(@"c:\test\testOutput.tif", codec, firstFrameParams);
// Сохранить в память - закомментируйте следующие три строки для работающего решения
using (MemoryStream tiffStream = new MemoryStream())
{
tiff.Save(tiffStream, codec, firstFrameParams);
// Для последующих страниц подготовить кодировщики
for (int page = 1; page < _imageCache.Count; page++)
tiff.SaveAdd(_imageCache[page], additionalFrameParams);
// Наконец, сбросить файл
tiff.SaveAdd(flushParams);
// Сохранить изображение в памяти в файл - закомментируйте следующие три строки для работающего решения
using (Image tiffOutput = Image.FromStream(tiffStream))
tiffOutput.Save(@"c:\test\testOutput.tif", codec, saveToFileParams);
}
}
}
}
Стоит отметить, что объект изображения tiffOutput, созданный из потока, НЕ содержит ожидаемых 3 рамок, а только 1 (первую) страницу. Похоже, что метод Image.SaveAdd
не работает с потоками памяти, хотя и указывается, что он работает.
Ответ или решение
Для сохранения многостраничного TIFF-изображения в объект MemoryStream
, важно правильно управлять последовательностью сохранения страниц и параметрами кодирования. Из вашего примера видно, что существует ошибка при работе с потоками и сохранении страниц. Давайте детально разберёмся, как это можно сделать правильно.
Шаги для сохранения многостраничного TIFF в MemoryStream
-
Загрузка изображений: Вы уже загружаете изображения в список, и это правильно. Обратите внимание на то, что изображения должны быть совместимы по размеру и глубине цвета, если они будут объединены в одну TIFF.
-
Настройка кодека: Правильный выбор кодека для TIFF является критически важным. Вы делаете это корректно, получая кодек по MIME-типу.
-
Создание параметров кодирования: Вам необходимо создать параметры кодирования для первого кадра, добавляемых кадров и для завершения. Это уже сделано, однако стоит уделить внимание правильному их использованию.
-
Сохранение в MemoryStream: Главное здесь – корректно использовать метод
SaveAdd
. Запишите многостраничное изображение в потокMemoryStream
, следя за тем, чтобы каждый кадр был добавлен, а затем завершите сохранение.
Пример кода
Ниже приведён обновлённый код для сохранения многостраничного TIFF в объект MemoryStream
и последующего сохранения в файл:
public void Test()
{
// Создаём список изображений
List<Image> imageCache = new List<Image>
{
Image.FromFile(@"c:\test\testInput1.tif"),
Image.FromFile(@"c:\test\testInput2.tif"),
Image.FromFile(@"c:\test\testInput3.tif")
};
// Получаем кодек для TIFF
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(c => c.MimeType == "image/tiff");
if (codec == null)
throw new Exception("Tiff codec not found.");
// Создаём параметры кодирования
EncoderParameters encoderParamsFirst = new EncoderParameters(2);
encoderParamsFirst.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
encoderParamsFirst.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
EncoderParameters encoderParamsAdd = new EncoderParameters(2);
encoderParamsAdd.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
encoderParamsAdd.Param[1] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
EncoderParameters encoderParamsLast = new EncoderParameters(1);
encoderParamsLast.Param[0] = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.LastFrame);
// Сохраняем изображения в MemoryStream
using (MemoryStream byteStream = new MemoryStream())
{
// Начинаем сохранять первое изображение
imageCache[0].Save(byteStream, ImageFormat.Tiff, encoderParamsFirst);
// Добавляем остальные изображения
for (int i = 1; i < imageCache.Count; i++)
{
imageCache[i].SaveAdd(byteStream, encoderParamsAdd);
}
// Завершаем процесс сохранения
imageCache[0].SaveAdd(encoderParamsLast);
// Теперь сохраняем MemoryStream в файл
using (FileStream fileStream = new FileStream(@"c:\test\testOutput.tif", FileMode.Create, FileAccess.Write))
{
byteStream.Position = 0; // Сбрасываем позицию потока
byteStream.CopyTo(fileStream);
}
}
}
Объяснение
-
Сохранение в
MemoryStream
: Теперь мы сохраняем все страницы вMemoryStream
, корректно добавляя каждую из них, начиная с первого. После добавления всех страниц мы завершаем процесс вызовомSaveAdd
с параметромLastFrame
. -
Сохранение в файл: После завершения сохранения мы переносим данные из
MemoryStream
в файл, сбрасывая позицию потока для правильного чтения.
Данный метод обеспечивает корректное сохранение всех страниц изображения в MemoryStream
, что позволяет избежать проблем, которые вы встречали ранее. Убедитесь, что все изображения имеют одинаковые параметры, чтобы избежать ошибок.