【问题标题】:How to reduce the memory usage when encoding base64 string in c#?在c#中编码base64字符串时如何减少内存使用?
【发布时间】:2014-03-30 20:27:03
【问题描述】:

我正在使用以下代码从位于我的计算机系统上的文件中检索位图:

Bitmap shellThumb = sf.Thumbnail.ExtraLargeBitmap;

其中 sf 是一个 ShellFile。

然后我使用以下将位图转换为base64:

public static string ToBase64String(this Bitmap bmp, ImageFormat imageFormat)
    {
        string base64String = string.Empty;


        MemoryStream memoryStream = new MemoryStream();
        bmp.Save(memoryStream, imageFormat);

        memoryStream.Position = 0;
        byte[] byteBuffer = memoryStream.ToArray();

        memoryStream.Close();

        base64String = Convert.ToBase64String(byteBuffer);
        byteBuffer = null;

        return base64String;
    }

然后我使用以下方法调用此方法:

string base64ImageAndTag = shellThumb.ToBase64String(ImageFormat.Png);

然后我使用 JSON.Net 将此字符串以及文件的其他属性写入一个 JSON 文件,以便在另一个项目中使用 - JSON 没有替代品,必须使用它。

string json = JsonConvert.SerializeObject(files.ToArray(), Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

其中 files 是一个列表。

我必须读取指定文件夹中每个文件的缩略图。

通过读取文件,我得到了 OutOfMemory 异常,并且在写入 JSON 文件时转换是导致此问题的原因。

有没有办法更好地编码位图以避免异常?我已经搜索了这个问题的解决方案,但不确定如何/在哪里应用任何可能的解决方案来完成这项工作。

非常感谢!!

【问题讨论】:

  • 你知道导致内存不足异常的真正原因吗? “更糟糕”的情况是将图像流式传输(通过 base64,带有自定义 base64-ICryptoTransform 的 CryptoStream 可能会有所帮助)将图像传输到文件。无需将整个文件(包括大量图像?)同时保存在内存中。
  • 嗨@Caramiriel,感谢您的评论!尝试写入 json 文件时发生异常。我知道它必须是 base64 字符串,因为只有在我添加它之后才会发生异常。字符串是文件对象的属性,因此需要使用其文件对象的属性写入 - 将整个文件和图像同时保存在内存中究竟是什么意思?我该如何解决这个问题?
  • 好吧,我不知道是否有这样的东西(还没有使用过),但是对于 XML,有 XmlWriter 直接写入流。这将允许您分别编写每个元素/属性。您的情况的一个示例:尝试序列化第二个图像时,第一个图像已经在磁盘上(不再在内存中)。也许 JSON 也有类似的东西。

标签: c# json shell bitmap base64


【解决方案1】:

减少内存占用的一种方法是一次序列化单个对象并将它们流式传输到输出中,而不是尝试预先在内存中生成所有对象(及其 Base64 字符串)。您将使用 Json.Net 中的 JsonSerializerJsonTextWriter 类一次一个地流式传输图像数据。下面是一个比较两种序列化方式的简短示例。您可能会将StringWriter 替换为指向输出文件的StreamWriter。在GenerateImageFile 内部,您可以调用ToBase64String 方法。

class ImageFile
{
    public string Name { get; set; }
    public string Base64Image { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var files = new[] { "test.bmp", "test2.bmp" };

        // generate all the objects and then serialize.
        var imageFiles = files.Select(GenerateImageFile);
        var serialized = JsonConvert.SerializeObject(imageFiles.ToArray(), Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
        Console.WriteLine(serialized);

        // generate objects one at a time.
        // use JsonSerialzer/JsonTextWriter to "stream" the objects
        var s = JsonSerializer.Create(new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
        var strWriter = new StringWriter();
        using (var writer = new JsonTextWriter(strWriter) { Formatting = Formatting.Indented })
        {
            writer.WriteStartArray();
            foreach(var file in files)
            {
                var imageFile = GenerateImageFile(file);
                s.Serialize(writer, imageFile);
            }
            writer.WriteEndArray();
        }
        Console.WriteLine(strWriter.GetStringBuilder());
    }

    private static ImageFile GenerateImageFile(string fileName)
    {
        return new ImageFile() { Name = fileName, Base64Image = fileName + fileName };
    }
}

【讨论】:

    【解决方案2】:

    你会想要压缩它。如果要保留位图格式,可以使用 gzip(或任何其他更适合的压缩器)压缩图像。这也将是无损压缩,您不会损失任何质量。

    如果您可以将其转换为 jpeg,那可能会给您带来更高的压缩率。不过要小心,这种压缩会导致质量损失。

    【讨论】:

    • 嗨@oleksii,感谢您的回答。我可以以编程方式压缩位图吗?我该怎么做??
    • @user2651192,当然可以。位图是一系列字节。您已经将其转换为内存流,然后可以对其进行压缩。请参阅this link,了解如何使用 gzip 压缩/解压缩流(在这种情况下,它是文件流,但没关系 - 任何流都可以工作)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-05
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多