Skip to content

Commit b2fe15c

Browse files
committed
fix: resolve DOCX conversion timeout for large files
- Added dynamic timeout calculation based on file size for conversion processes - Unzip timeout: 30s base + 2s per MB - Pandoc timeout: 60s base + 5s per MB - HTML to PDF timeout: 120s base + 10s per MB - Added logging for calculated timeouts to aid debugging Log: resolve DOCX conversion timeout for large files pms: BUG-351285
1 parent b74de38 commit b2fe15c

1 file changed

Lines changed: 44 additions & 4 deletions

File tree

reader/document/Model.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -23,6 +23,16 @@
2323

2424
namespace {
2525

26+
// DOCX conversion timeout constants (in milliseconds)
27+
static constexpr int UNZIP_BASE_TIMEOUT = 30000; // 30 seconds base timeout for unzip
28+
static constexpr int UNZIP_TIMEOUT_PER_MB = 2000; // 2 seconds per MB for unzip
29+
static constexpr int PANDOC_BASE_TIMEOUT = 60000; // 60 seconds base timeout for pandoc
30+
static constexpr int PANDOC_TIMEOUT_PER_MB = 5000; // 5 seconds per MB for pandoc
31+
static constexpr int HTMLTOPDF_BASE_TIMEOUT = 120000; // 120 seconds base timeout for html to pdf
32+
static constexpr int HTMLTOPDF_TIMEOUT_PER_MB = 10000; // 10 seconds per MB for html to pdf
33+
static constexpr int BYTES_PER_MB = 1024 * 1024; // Bytes in one megabyte
34+
static constexpr int MAX_TIMEOUT_MS = 600000; // Maximum timeout: 10 minutes
35+
2636
QString getHtmlToPdfPath() {
2737

2838
QString path = QString(INSTALL_PREFIX) + "/lib/deepin-reader/htmltopdf";
@@ -102,6 +112,36 @@ deepin_reader::Document *deepin_reader::DocumentFactory::getDocument(const int &
102112
return nullptr;
103113
}
104114

115+
// Calculate dynamic timeout based on file size
116+
qint64 fileSizeInMB = file.size() / BYTES_PER_MB;
117+
118+
auto calculateTimeout = [](qint64 sizeInMB, int baseTimeout, int perMbTimeout) -> int {
119+
// Ensure base timeout is returned for zero or negative size
120+
if (sizeInMB <= 0) {
121+
return baseTimeout;
122+
}
123+
124+
// Check for potential overflow before multiplication
125+
qint64 maxSafeSize = (MAX_TIMEOUT_MS - baseTimeout) / perMbTimeout;
126+
if (sizeInMB > maxSafeSize) {
127+
qCWarning(appLog) << "File size" << sizeInMB << "MB exceeds safe calculation limit, using max timeout";
128+
return MAX_TIMEOUT_MS;
129+
}
130+
131+
// Safe to calculate now
132+
qint64 calculated = sizeInMB * perMbTimeout + baseTimeout;
133+
int timeout = static_cast<int>(qMin(calculated, static_cast<qint64>(MAX_TIMEOUT_MS)));
134+
135+
return qMax(baseTimeout, timeout);
136+
};
137+
138+
int unzipTimeout = calculateTimeout(fileSizeInMB, UNZIP_BASE_TIMEOUT, UNZIP_TIMEOUT_PER_MB);
139+
int pandocTimeout = calculateTimeout(fileSizeInMB, PANDOC_BASE_TIMEOUT, PANDOC_TIMEOUT_PER_MB);
140+
int htmlToPdfTimeout = calculateTimeout(fileSizeInMB, HTMLTOPDF_BASE_TIMEOUT, HTMLTOPDF_TIMEOUT_PER_MB);
141+
142+
qCInfo(appLog) << "File size:" << fileSizeInMB << "MB, Timeouts - unzip:" << unzipTimeout/1000
143+
<< "s, pandoc:" << pandocTimeout/1000 << "s, htmltopdf:" << htmlToPdfTimeout/1000 << "s";
144+
105145
//解压的目的是为了让资源文件可以被转换的时候使用到,防止转换后丢失图片等媒体信息
106146
QProcess decompressor;
107147
*pprocess = &decompressor;
@@ -117,7 +157,7 @@ deepin_reader::Document *deepin_reader::DocumentFactory::getDocument(const int &
117157
*pprocess = nullptr;
118158
return nullptr;
119159
}
120-
if (!decompressor.waitForFinished()) {
160+
if (!decompressor.waitForFinished(unzipTimeout)) {
121161
qCritical() << "Unzip process failed for file:" << targetDoc;
122162
error = deepin_reader::Document::ConvertFailed;
123163
*pprocess = nullptr;
@@ -158,7 +198,7 @@ deepin_reader::Document *deepin_reader::DocumentFactory::getDocument(const int &
158198
*pprocess = nullptr;
159199
return nullptr;
160200
}
161-
if (!converter.waitForFinished()) {
201+
if (!converter.waitForFinished(pandocTimeout)) {
162202
qCritical() << "Pandoc conversion process failed";
163203
error = deepin_reader::Document::ConvertFailed;
164204
*pprocess = nullptr;
@@ -192,7 +232,7 @@ deepin_reader::Document *deepin_reader::DocumentFactory::getDocument(const int &
192232
*pprocess = nullptr;
193233
return nullptr;
194234
}
195-
if (!converter2.waitForFinished()) {
235+
if (!converter2.waitForFinished(htmlToPdfTimeout)) {
196236
qCritical() << "Htmltopdf conversion process failed";
197237
error = deepin_reader::Document::ConvertFailed;
198238
*pprocess = nullptr;

0 commit comments

Comments
 (0)