I built an ASP.NET MVC API hosted on IIS on Windows 10 Pro (VM on Azure - 4GB RAM, 2CPU). Within I call an .exe (wkhtmltopdf) that I want to convert an HTML page to image and save it locally. Everything works fine, except I noticed that after some calls to the API, the RAM goes crazy and while investigating the process with Task Manager I saw a process, called IIS Worker Process, that adds more RAM every time the API is called. Of course I wrapped my System.Diagnostics.Process instance usage inside a using statement to be disposed, because IDisposable is implemented, but it still consumes more and more RAM and after a while the server becomes laggy and unresponsive (it has only 4GB of RAM after all). I noticed that after some number of minutes (10-15-20 maybe) this IIS Worker Process calms down in terms of RAM usage... Here is my code, pretty straight forward:
- Gets base64 encoded url
- Decodes it
- Uses wkhtmltoimage.exe to convert it to image
- Saves it locally
- Reads the byte array
- Creates a blob in Azure with the image
Returns json with the url
public async Task<ActionResult> Index(string url) { object oJSON = new { url = string.Empty }; if (!string.IsNullOrEmpty(value: url)) { try { byte[] EncodedData = Convert.FromBase64String(s: url); string DecodedURL = Encoding.UTF8.GetString(bytes: EncodedData); using (Process proc = new Process()) { proc.StartInfo.FileName = wkhtmltopdfExecutablePath; proc.StartInfo.Arguments = $"--encoding utf-8 \"{DecodedURL}\" {LocalImageFilePath}"; proc.Start(); proc.WaitForExit(); oJSON = new { procStatusCode = proc.ExitCode }; } if (System.IO.File.Exists(path: LocalImageFilePath)) { byte[] pngBytes = System.IO.File.ReadAllBytes(path: LocalImageFilePath); System.IO.File.Delete(path: LocalImageFilePath); string ImageURL = await CreateBlob(blobName: $"{BlobName}.png", data: pngBytes); oJSON = new { url = ImageURL }; } } catch (Exception ex) { Debug.WriteLine(value: ex); } } return Json(data: oJSON, behavior: JsonRequestBehavior.AllowGet); } private async Task<string> CreateBlob(string blobName, byte[] data) { string ConnectionString = "DefaultEndpointsProtocol=https;AccountName=" + AzureStorrageAccountName + ";AccountKey=" + AzureStorageAccessKey + ";EndpointSuffix=core.windows.net"; CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(connectionString: ConnectionString); CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName: AzureBlobContainer); await cloudBlobContainer.CreateIfNotExistsAsync(); BlobContainerPermissions blobContainerPermissions = await cloudBlobContainer.GetPermissionsAsync(); blobContainerPermissions.PublicAccess = BlobContainerPublicAccessType.Container; await cloudBlobContainer.SetPermissionsAsync(permissions: blobContainerPermissions); CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(blobName: blobName); cloudBlockBlob.Properties.ContentType = "image/png"; using (Stream stream = new MemoryStream(buffer: data)) { await cloudBlockBlob.UploadFromStreamAsync(source: stream); } return cloudBlockBlob.Uri.AbsoluteUri; }
Here are the resources I'm reading somehow related to this issue IMO, but are not helping much:
Investigating ASP.Net Memory Dumps for Idiots (like Me)
ASP.NET app eating memory. Application / Session objects the reason?
IIS Worker Process using a LOT of memory?
Run dispose method upon asp.net IIS app restart
UPDATE:
if (System.IO.File.Exists(path: LocalImageFilePath))
{
string BlobName = Guid.NewGuid().ToString(format: "n");
string ImageURL = string.Empty;
using(FileStream fileStream = new FileStream(LocalImageFilePath, FileMode.Open)
{
ImageURL = await CreateBlob(blobName: $"{BlobName}.png", dataStream: fileStream);
}
System.IO.File.Delete(path: LocalImageFilePath);
oJSON = new { url = ImageURL };
}