Skip to content

Commit 30c274f

Browse files
committed
Fix issue causing high CPU usage when opening large images
1 parent a0c80ca commit 30c274f

File tree

4 files changed

+89
-35
lines changed

4 files changed

+89
-35
lines changed

Tiefsee/Adapter.cs

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Tiefsee;
1+
namespace Tiefsee;
22

33
public static class Adapter {
44

@@ -102,4 +102,18 @@ private static void ThreadSleep(int interval) {
102102
}
103103
}
104104

105+
/// <summary>
106+
/// 超時就強制結束
107+
/// </summary>
108+
/// <param name="timeoutSeconds"> 最長秒數 </param>
109+
/// <param name="func"></param>
110+
public static void RunWithTimeout(double timeoutSeconds, Action func) {
111+
CancellationTokenSource cts = new();
112+
cts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds)); // 設定超時時間
113+
114+
Task task = Task.Run(func, cts.Token); // 將 CancellationToken 傳遞給 Task.Run
115+
116+
task.Wait(cts.Token); // 等待任務完成或超時
117+
}
118+
105119
}

Tiefsee/Lib/ImgLib.cs

+12-9
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,10 @@ public static Bitmap GetFileIcon(string path, int size) {
2626

2727
Bitmap icon = null;
2828

29-
CancellationTokenSource cts = new();
30-
cts.CancelAfter(TimeSpan.FromSeconds(1)); // 設定超時時間為一秒
31-
32-
Task task = Task.Run(() => {
29+
Adapter.RunWithTimeout(1, () => {
3330
// 取得圖片在Windows系統的縮圖
3431
icon = WindowsThumbnailProvider.GetThumbnail(path, size, size, ThumbnailOptions.ScaleUp);
35-
}, cts.Token); // 將 CancellationToken 傳遞給 Task.Run
36-
37-
task.Wait(cts.Token); // 等待任務完成或超時
32+
});
3833

3934
return icon;
4035
}
@@ -801,9 +796,17 @@ public static string VipsResize(string path, double scale, string fileType, stri
801796
}
802797

803798
NetVips.Image im = GetNetVips(img100, fileType);
804-
using (NetVips.Image imR = im.Resize(scale: scale, kernel: Enums.Kernel.Lanczos3, gap: 4)) {
805-
VipsSave(imR, filePath, "auto");
799+
800+
Enums.Kernel? kernel = Enums.Kernel.Lanczos3;
801+
double? gap = 4;
802+
// 如果圖片太大,就使用計算成本較低的 最近鄰法
803+
if (im.Width > 30000 || im.Height > 30000) {
804+
kernel = Enums.Kernel.Nearest;
805+
gap = null;
806806
}
807+
using NetVips.Image imR = im.Resize(scale: scale, kernel: kernel, gap: gap);
808+
VipsSave(imR, filePath, "auto");
809+
807810
StartWindow.isRunGC = true; // 定時執行GC
808811

809812
return filePath;

Tiefsee/Server/WebServerController.cs

+49-25
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,20 @@ void ImgVipsInit(RequestData d) {
108108
//bool is304 = HeadersAdd304(d, path); //回傳檔案時加入快取的Headers
109109
//if (is304 == true) { return; }
110110

111-
for (int i = 0; i < arType.Length; i++) {
112-
string type = arType[i];
111+
Adapter.RunWithTimeout(60, () => {
112+
for (int i = 0; i < arType.Length; i++) {
113+
string type = arType[i];
113114

114-
try {
115-
imgInfo = ImgLib.GetImgInitInfo(path, type, type);
116-
}
117-
catch { }
115+
try {
116+
imgInfo = ImgLib.GetImgInitInfo(path, type, type);
117+
}
118+
catch { }
118119

119-
if (imgInfo.width != 0) {
120-
break;
120+
if (imgInfo.width != 0) {
121+
break;
122+
}
121123
}
122-
}
124+
});
123125

124126
json = JsonSerializer.Serialize(imgInfo);
125127
WriteString(d, json); // 回傳輸出的檔案路徑
@@ -139,8 +141,11 @@ void ImgVipsResize(RequestData d) {
139141
bool is304 = HeadersAdd304(d, path); // 回傳檔案時加入快取的Headers
140142
if (is304) { return; }
141143

142-
string imgPath = ImgLib.VipsResize(path, scale, fileType, vipsType);
143-
WriteFile(d, imgPath); // 回傳檔案
144+
Adapter.RunWithTimeout(60, () => {
145+
string imgPath = ImgLib.VipsResize(path, scale, fileType, vipsType);
146+
WriteFile(d, imgPath); // 回傳檔案
147+
});
148+
144149
}
145150

146151
/// <summary>
@@ -374,27 +379,46 @@ private void GetFileIcon(RequestData d) {
374379
bool is304 = HeadersAdd304(d, path); // 回傳檔案時加入快取的 Headers
375380
if (is304) { return; }
376381

382+
Bitmap icon = null;
383+
377384
try {
378-
using Bitmap icon = ImgLib.GetFileIcon(path, size);
379-
if (icon == null) { return; }
385+
icon = ImgLib.GetFileIcon(path, size);
386+
}
387+
catch { }
388+
389+
// 如果取得失敗,就等待 1 秒後再試一次
390+
if (icon == null) {
391+
Thread.Sleep(1000);
392+
try {
393+
icon = ImgLib.GetFileIcon(path, size);
394+
}
395+
catch { }
396+
}
397+
398+
// 如果 2 次都取得失敗,就返回 500 錯誤
399+
if (icon == null) {
400+
d.context.Response.StatusCode = 500;
401+
WriteString(d, "500");
402+
return;
403+
}
380404

381-
using (Stream input = new MemoryStream()) {
405+
try {
406+
using Stream input = new MemoryStream();
382407

383-
icon.Save(input, System.Drawing.Imaging.ImageFormat.Png);
384-
input.Position = 0;
408+
icon.Save(input, System.Drawing.Imaging.ImageFormat.Png);
409+
input.Position = 0;
385410

386-
d.context.Response.ContentLength64 = input.Length;
411+
d.context.Response.ContentLength64 = input.Length;
387412

388-
if (d.context.Request.HttpMethod != "HEAD") {
389-
byte[] buffer = new byte[1024 * 16];
390-
int nbytes;
391-
while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0) {
392-
// context.Response.SendChunked = input.Length > 1024 * 16;
393-
d.context.Response.OutputStream.Write(buffer, 0, nbytes);
394-
}
413+
if (d.context.Request.HttpMethod != "HEAD") {
414+
byte[] buffer = new byte[1024 * 16];
415+
int nbytes;
416+
while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0) {
417+
// context.Response.SendChunked = input.Length > 1024 * 16;
418+
d.context.Response.OutputStream.Write(buffer, 0, nbytes);
395419
}
396420
}
397-
421+
icon.Dispose();
398422
}
399423
catch {
400424
d.context.Response.StatusCode = 500;

Www/ts/Tiefseeview.ts

+13
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,7 @@ class Tiefseeview {
26272627

26282628
let _w = toNumber(dom_data.style.width); // 原始圖片大小(旋轉前的大小)
26292629
let _h = toNumber(dom_data.style.height);
2630+
if (_w === 0 || _h === 0) { return; }
26302631
let _margin = 35; // 多繪製的區域
26312632
let _scale = _w / getOriginalWidth(); // 目前的 圖片縮放比例
26322633
let radio_can = 1;
@@ -2726,6 +2727,18 @@ class Tiefseeview {
27262727
dWidth: dWidth, dHeight: dHeight
27272728
}
27282729

2730+
// 如果圖片大於 canvas 的最大限制,就改用 <img> 來渲染
2731+
let nowWidth = can.width;
2732+
let nowHeight = can.height;
2733+
if (nowWidth > 65535 || nowHeight > 65535 || nowWidth * nowHeight > 265690000) {
2734+
console.log("圖片過大,無法渲染", url);
2735+
setDataType("img");
2736+
await loadImg(url);
2737+
// 載入圖片後必須重新設定大小
2738+
setDataSize(dom_data.offsetWidth);
2739+
return;
2740+
}
2741+
27292742
// if (sx < 0) { sx = 0 }
27302743
// if (sy < 0) { sy = 0 }
27312744
// if (sWidth > getOriginalWidth()) { sWidth = getOriginalWidth() }

0 commit comments

Comments
 (0)