Skip to content

Commit

Permalink
fix: 修复读取超大文件时软件闪退的问题 (#211)
Browse files Browse the repository at this point in the history
Description: 文本编辑器采用直接加载文件的处理方式,打开超过系统
内存的文件会导致申请内存失败。
添加异常捕获处理,在出现文本读取错误时提示文件可能过大或损坏。
添加新增提示的翻译。

Log: 修复读取超大文件时软件闪退的问题
Bug: https://pms.uniontech.com/bug-view-184107.html
Influence: 文件读取 翻译
  • Loading branch information
rb-union authored Feb 1, 2023
1 parent 218dbee commit d6fa196
Show file tree
Hide file tree
Showing 34 changed files with 6,640 additions and 6,451 deletions.
19 changes: 14 additions & 5 deletions src/common/fileloadthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,18 @@ void FileLoadThread::run()
}
}

// reads all remaining data from the file.
indata += file.read(file.size());
file.close();
// 读取申请开辟内存空间时,捕获可能出现的 std::bad_alloc() 异常,防止闪退。
try {
// reads all remaining data from the file.
indata += file.read(file.size());
file.close();
} catch (const std::exception &e) {
qWarning() << Q_FUNC_INFO << "Read file data error, " << QString(e.what());

file.close();
emit sigLoadFinished(encode, indata, true);
return;
}

if (encode.isEmpty()) {
//编码识别,如果文件数据大于1M,则只裁剪出1M文件数据去做编码探测
Expand All @@ -62,11 +71,11 @@ void FileLoadThread::run()

QString textEncode = QString::fromLocal8Bit(encode);
if (textEncode.contains("ASCII", Qt::CaseInsensitive) || textEncode.contains("UTF-8", Qt::CaseInsensitive)) {
emit sigLoadFinished(encode, indata);
emit sigLoadFinished(encode, indata, false);
} else {
QByteArray outData;
DetectCode::ChangeFileEncodingFormat(indata, outData, textEncode, QString("UTF-8"));
emit sigLoadFinished(encode, outData);
emit sigLoadFinished(encode, outData, false);
}
}
}
2 changes: 1 addition & 1 deletion src/common/fileloadthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FileLoadThread : public QThread
signals:
// 预处理信号,优先处理文件头,防止出现加载时间过长的情况
void sigPreProcess(const QByteArray &encode, const QByteArray &content);
void sigLoadFinished(const QByteArray &encode, const QByteArray &content);
void sigLoadFinished(const QByteArray &encode, const QByteArray &content, bool error = false);

private:
QString m_strFilePath;
Expand Down
6 changes: 6 additions & 0 deletions src/controls/warningnotices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ void WarningNotices::setSaveAsBtn()
setWidget(m_saveAsBtn);
}

void WarningNotices::clearBtn()
{
m_saveAsBtn->setVisible(false);
m_reloadBtn->setVisible(false);
}

void WarningNotices::slotreloadBtnClicked()
{
this->hide();
Expand Down
1 change: 1 addition & 0 deletions src/controls/warningnotices.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class WarningNotices : public DFloatingMessage

void setReloadBtn();
void setSaveAsBtn();
void clearBtn();

signals:
void reloadBtnClicked();
Expand Down
12 changes: 9 additions & 3 deletions src/editor/dtextedit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3602,7 +3602,7 @@ QString TextEdit::getWordAtMouse()
}
}

void TextEdit::toggleReadOnlyMode()
void TextEdit::toggleReadOnlyMode(bool notNotify)
{
if (m_readOnlyMode) {
if (m_cursorMode == Overwrite) {
Expand All @@ -3614,14 +3614,20 @@ void TextEdit::toggleReadOnlyMode()
m_readOnlyMode = false;
setCursorWidth(1);
updateHighlightLineSelection();
popupNotify(tr("Read-Only mode is off"));

if (!notNotify) {
popupNotify(tr("Read-Only mode is off"));
}
} else {
m_readOnlyMode = true;
setReadOnly(true);
setCursorWidth(0); //隐藏光标
document()->clearUndoRedoStacks();
updateHighlightLineSelection();
popupNotify(tr("Read-Only mode is on"));

if (!notNotify) {
popupNotify(tr("Read-Only mode is on"));
}
emit cursorModeChanged(Readonly);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/editor/dtextedit.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class TextEdit : public DPlainTextEdit
void completionWord(QString word);
QString getWordAtMouse();
QString getWordAtCursor();
void toggleReadOnlyMode();
void toggleReadOnlyMode(bool notNotify = false);
void toggleComment(bool bValue);
int getNextWordPosition(QTextCursor &cursor, QTextCursor::MoveMode moveMode);
int getPrevWordPosition(QTextCursor cursor, QTextCursor::MoveMode moveMode);
Expand Down
70 changes: 43 additions & 27 deletions src/editor/editwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,45 +850,48 @@ void EditWrapper::handleFilePreProcess(const QByteArray &encode, const QByteArra
* @param encode 文件编码
* @param content 完整文件内容
*/
void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteArray &content)
void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error)
{
// 判断是否预加载,若已预加载,则无需重新初始化
if (!m_bHasPreProcess) {
reinitOnFileLoad(encode);
}

bool flag = m_pTextEdit->getReadOnlyPermission();
if (flag == true) {
// note: 特殊处理,由于需要TextEdit处于可编辑状态追加文件数据,临时设置非只读状态
m_pTextEdit->setReadOnly(false);
}
if (!error) {
bool flag = m_pTextEdit->getReadOnlyPermission();
if (flag == true) {
// note: 特殊处理,由于需要TextEdit处于可编辑状态追加文件数据,临时设置非只读状态
m_pTextEdit->setReadOnly(false);
}

m_bFileLoading = true;
m_bFileLoading = true;

//备份显示修改状态
if (m_bIsTemFile) {
updateModifyStatus(true);
}
//备份显示修改状态
if (m_bIsTemFile) {
updateModifyStatus(true);
}

// 判断处理前后对象状态
QPointer<QObject> checkPtr(this);
// 加载数据
loadContent(content);
if (checkPtr.isNull()) {
return;
}
// 判断处理前后对象状态
QPointer<QObject> checkPtr(this);
// 加载数据
loadContent(content);
if (checkPtr.isNull()) {
return;
}

//先屏蔽,双字节空字符先按照显示字符编码号处理
//clearDoubleCharaterEncode();
//PerformanceMonitor::openFileFinish(filePath(), QFileInfo(filePath()).size());
m_bFileLoading = false;
if (flag == true) {
m_pTextEdit->setReadOnly(true);
}

m_bFileLoading = false;
if (flag == true) {
m_pTextEdit->setReadOnly(true);
}
if (m_bQuit) {
return;
if (m_bQuit) {
return;
}
} else {
// 清除之前读取的数据
m_pTextEdit->clear();
}

m_pTextEdit->setTextFinished();

QStringList temFileList = Settings::instance()->settings->option("advance.editor.browsing_history_temfile")->value().toStringList();
Expand Down Expand Up @@ -936,6 +939,19 @@ void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteAr
}

m_pBottomBar->setEncodeName(m_sCurEncode);

// 提示读取错误信息
if (error) {
// 设置文本为只读模式,且不显示通知
if (!m_pTextEdit->getReadOnlyMode()) {
m_pTextEdit->toggleReadOnlyMode(true);
}

m_pWaringNotices->setMessage(tr("The file cannot be read, which may be too large or has been damaged!"));
m_pWaringNotices->clearBtn();
m_pWaringNotices->show();
DMessageManager::instance()->sendMessage(m_pTextEdit, m_pWaringNotices);
}
}


Expand Down
2 changes: 1 addition & 1 deletion src/editor/editwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class EditWrapper : public QWidget
public slots:
// 处理文档预加载数据
void handleFilePreProcess(const QByteArray &encode, const QByteArray &content);
void handleFileLoadFinished(const QByteArray &encode, const QByteArray &content);
void handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error);
void OnThemeChangeSlot(QString theme);
void UpdateBottomBarWordCnt(int cnt);
void OnUpdateHighlighter();
Expand Down
51 changes: 45 additions & 6 deletions tests/src/common/ut_fileloadthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#include "ut_fileloadthread.h"
#include "../../src/common/fileloadthread.h"
#include "stub.h"

#include <QFile>

test_fileloadthread::test_fileloadthread()
{
Expand All @@ -13,22 +16,22 @@ void test_fileloadthread::SetUp()
{
fthread = new FileLoadThread("aa");

EXPECT_NE(fthread,nullptr);
EXPECT_NE(fthread, nullptr);

}

void test_fileloadthread::TearDown()
{
delete fthread;
fthread=nullptr;
fthread = nullptr;
}

//FileLoadThread(const QString &filepath, QObject *QObject = nullptr);
TEST_F(test_fileloadthread, FileLoadThread)
{
FileLoadThread thread("aa");

EXPECT_EQ(thread.m_strFilePath,"aa");
EXPECT_EQ(thread.m_strFilePath, "aa");
}

//void run();
Expand All @@ -37,7 +40,7 @@ TEST_F(test_fileloadthread, run)
FileLoadThread *thread = new FileLoadThread("aa");
thread->run();

EXPECT_NE(thread,nullptr);
EXPECT_NE(thread, nullptr);
thread->deleteLater();
}

Expand All @@ -49,7 +52,7 @@ TEST_F(test_fileloadthread, setEncodeInfo)
//thread->setEncodeInfo(pathList,codeList);


EXPECT_NE(thread,nullptr);
EXPECT_NE(thread, nullptr);
thread->deleteLater();
}

Expand All @@ -59,6 +62,42 @@ TEST_F(test_fileloadthread, getCodec)
FileLoadThread *thread = new FileLoadThread("aa");
// thread->getCodec();

EXPECT_NE(thread,nullptr);
EXPECT_NE(thread, nullptr);
thread->deleteLater();
}

QByteArray readErrorFunc(qint64 maxlen)
{
Q_UNUSED(maxlen);
throw std::bad_alloc();
return QByteArray();
}

TEST_F(test_fileloadthread, readError)
{
QString tmpFilePath("/tmp/test_fileloadthread.txt");
QFile tmpFile(tmpFilePath);
if (tmpFile.open(QFile::WriteOnly)) {
tmpFile.write("local test data");
tmpFile.close();
}
ASSERT_TRUE(tmpFile.exists());

FileLoadThread *thread = new FileLoadThread(tmpFilePath);
Stub readErrorStub;
readErrorStub.set((QByteArray(QFile::*)(qint64))(ADDR(QFile, read)), readErrorFunc);

bool readError = false;
connect(thread, &FileLoadThread::sigLoadFinished, [&](const QByteArray & encode, const QByteArray & content, bool error) {
Q_UNUSED(encode);
Q_UNUSED(content);
readError = error;
});

thread->run();

EXPECT_TRUE(readError);
EXPECT_NE(thread, nullptr);
thread->deleteLater();
tmpFile.remove();
}
22 changes: 19 additions & 3 deletions tests/src/editor/ut_editwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ TEST(UT_Editwrapper_handleFileLoadFinished, UT_Editwrapper_handleFileLoadFinishe
setPrintEnabled_stub.set(ADDR(Window, setPrintEnabled), handleFileLoadFinished_001_setPrintEnabled_stub);
Stub setTextFinished_stub;
setTextFinished_stub.set(ADDR(TextEdit, setTextFinished), handleFileLoadFinished_001_setTextFinished_stub);
pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent);
pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent, false);
ASSERT_TRUE(pWindow->currentWrapper()->m_pBottomBar->m_pEncodeMenu != nullptr);

pWindow->deleteLater();
Expand Down Expand Up @@ -734,7 +734,7 @@ TEST(UT_Editwrapper_handleFileLoadFinished, UT_Editwrapper_handleFileLoadFinishe
stringList.push_back(c2);


pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent);
pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent, false);
ASSERT_TRUE(pWindow->currentWrapper()->m_pBottomBar->m_pEncodeMenu != nullptr);

pWindow->deleteLater();
Expand Down Expand Up @@ -781,14 +781,30 @@ TEST(UT_Editwrapper_handleFileLoadFinished, UT_Editwrapper_handleFileLoadFinishe
stringList.push_back(c2);


pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent);
pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent, false);
ASSERT_TRUE(pWindow->currentWrapper()->m_pBottomBar->m_pEncodeMenu != nullptr);

pWindow->deleteLater();
delete d;
d = nullptr;
}

TEST(UT_Editwrapper_handleFileLoadFinished, UT_Editwrapper_handleFileLoadFinished_004_error)
{
Window *pWindow = new Window();
pWindow->addBlankTab(QString());
const QString filePath = QCoreApplication::applicationDirPath() + QString("/Makefile");
QByteArray encode = QByteArray();
const QByteArray retFileContent = FileLoadThreadRun(filePath, &encode);
Stub setPrintEnabled_stub;
setPrintEnabled_stub.set(ADDR(Window, setPrintEnabled), handleFileLoadFinished_001_setPrintEnabled_stub);
Stub setTextFinished_stub;
setTextFinished_stub.set(ADDR(TextEdit, setTextFinished), handleFileLoadFinished_001_setTextFinished_stub);
pWindow->currentWrapper()->handleFileLoadFinished(encode, retFileContent, true);
EXPECT_TRUE(pWindow->currentWrapper()->textEditor()->getReadOnlyPermission());

pWindow->deleteLater();
}

////重新加载文件编码 1.文件修改 2.文件未修改处理逻辑一样 切换编码重新加载和另存为 梁卫东
//bool reloadFileEncode(QByteArray encode);
Expand Down
Loading

0 comments on commit d6fa196

Please sign in to comment.