【发布时间】:2020-02-17 13:00:15
【问题描述】:
TL;DR;转换为 base64string 的图像在大型对象堆中占用大量 RAM。
我在 Windows 服务中有一些代码,它使用用户上传的我们的产品图像,将它们标准化为网络级格式(他们将上传 10MB 位图),并执行一些其他操作,例如将它们调整为正方形并添加空格填充。
然后它将它们转换为 base64 字符串,以通过 rest 将它们上传到我们的托管环境中。环境要求这样做,我不能使用 URLS。当我这样做时,它们会存储在大型对象堆中,并且随着时间的推移,程序的 RAM 使用量会猛增。
我该如何解决这个问题?
这里是代码,
private void HandleDocuments(IBaseProduct netforumProduct, MagentoClient client, bool isChild)
{
if (netforumProduct.Documents == null) { return; }
for (int idx = 0; idx < netforumProduct.Documents.Count; idx++)
{
JToken document = netforumProduct.Documents[idx]["Document"];
if (document == null) { continue; }
string fileName = document["URL"].ToString();
// Skip photos on child products (the only identifier is part of the url string)
if (fileName.ToLower().Contains("photo") && isChild) { continue; }
using (HttpClient instance = new HttpClient {BaseAddress = client.NetforumFilesBaseAddress})
{
string trimStart = fileName.TrimStart('.');
string base64String;
using (Stream originalImageStream = instance.GetStreamAsync("iweb" + trimStart).Result)
{
using (MemoryStream newMemoryStream = new MemoryStream())
{
using (Image img = Image.FromStream(originalImageStream))
{
using (Image retImg = Utility.Framework.ImageToFixedSize(img, 1200, 1200))
{
retImg.Save(newMemoryStream, ImageFormat.Jpeg);
}
}
newMemoryStream.Position = 0;
byte[] bytes = newMemoryStream.ToArray();
base64String = Convert.ToBase64String(bytes);
}
}
// MediaGalleryEntry is a simple class with a few string properties
MediaGalleryEntry mge = new MediaGalleryEntry
{
label = "Product_" + netforumProduct.Code + "_image_" + idx,
content = new MediaGalleryContent
{
base64_encoded_data = base64String,
name = "Gallery_Image_" + idx
},
file = trimStart
};
this.media_gallery_entries.Add(mge);
}
}
}
它不是有史以来最好的代码,可能没有高度优化,但它是我能做到的最好的。
【问题讨论】:
-
好吧,一个 10 MB 的位图可能会变成一个 1 MB 的 JPEG,而后者又会变成一个 1.3 MB 的 base64 字符串。在
this.media_gallery_entries.Add(mge),您保留对此字符串的引用,因此它不能被垃圾回收。这是你的问题吗? -
是的,这正是问题所在。一旦我发布到网络服务,我真的不确定如何处理这个字符串。
-
@CarComp 只需停止引用它,GC 就会最终收集它。不需要特殊处理,因为它只是一个字符串。
-
根据您构建 Web 请求的方式,构建文件(即通过流式传输,而不是通过构建字符串然后写入)并上传它可能会绕过这个(甚至绕过文件并直接流式传输到 URL,但这可能会更复杂,具体取决于 API)。这意味着不使用
Convert,而是使用支持流likeToBase64Transform的东西。 -
您可能是指提供(或生成)的调用 REST API 的编程 API 可与字符串一起使用,但这可能并不意味着不可能编写没有此缺陷的新 API。 REST 的全部特点是它很简单,因此任何语言都可以使用 API。最终,这一切都以 TCP 上的字节流的形式结束,因此在 C# 中仅从物理端点构造大字符串没有(不可能)硬性要求。它可能就像给
MediaGalleryEntry一个Stream类型的属性一样简单。
标签: c# heap-memory tobase64string