【FileZilla】上传下载时文件夹的处理
先看一个下载动作的调用轨迹(下载一个或多个文件或文件夹):
// Function bool CQueueView::TryStartNextTransfer() in QueueView.cpp
while (newFileItem && newFileItem->Download() && newFileItem->GetType() == QueueItemType::Folder) {CLocalPath localPath(newFileItem->GetLocalPath());localPath.AddSegment(newFileItem->GetLocalFile());wxFileName::Mkdir(localPath.GetPath(), 0777, wxPATH_MKDIR_FULL);const std::vector<CState*> *pStates = CContextManager::Get()->GetAllStates();for (auto & state : *pStates) {state->RefreshLocalFile(localPath.GetPath());}if (RemoveItem(newFileItem, true)) {// Server got deleted. Unfortunately we have to start over nowif (m_serverList.empty()) {return false;}return true;}newFileItem = currentServerItem->GetIdleChild(m_activeMode == 1, wantedDirection);
}
这段代码从队列中拿出一个项,如果是文件夹的话,就在本地创建文件夹并删除这个项(下载语境)。那么问题来了,这个文件夹里的文件们是什么时候放进队列里的呢?
void local_recursive_operation::thread_entry()
{{fz::scoped_lock l(mutex_);auto filters = m_filters.first;while (!recursion_roots_.empty()) {listing d;bool recurse{true};{auto& root = recursion_roots_.front();if (root.m_dirsToVisit.empty()) {recursion_roots_.pop_front();continue;}auto const& dir = root.m_dirsToVisit.front();d.localPath = dir.localPath;d.remotePath = dir.remotePath;recurse = dir.recurse;root.m_dirsToVisit.pop_front();}// Do the slow part without holding mutexl.unlock();bool sentPartial = false;fz::local_filesys fs;fz::native_string localPath = fz::to_native(d.localPath.GetPath());if (fs.begin_find_files(localPath)) {listing::entry entry;bool isLink{};fz::native_string name;fz::local_filesys::type t{};while (fs.get_next_file(name, isLink, t, &entry.size, &entry.time, &entry.attributes)) {if (isLink && m_ignoreLinks) {continue;}entry.name = fz::to_wstring(name);if (!filter_manager::FilenameFiltered(filters, entry.name, d.localPath.GetPath(), t == fz::local_filesys::dir, entry.size, entry.attributes, entry.time)) {if (t == fz::local_filesys::dir) {d.dirs.emplace_back(std::move(entry));}else {d.files.emplace_back(std::move(entry));}// If having queued 5k items, hand off to main thread.if (d.files.size() + d.dirs.size() >= 5000) {sentPartial = true;listing next;next.localPath = d.localPath;next.remotePath = d.remotePath;l.lock();// Check for cancellationif (recursion_roots_.empty()) {l.unlock();break;}EnqueueEnumeratedListing(l, std::move(d), recurse);l.unlock();d = next;}}}}l.lock();// Check for cancellationif (recursion_roots_.empty()) {break;}if (!sentPartial || !d.files.empty() || !d.dirs.empty()) {EnqueueEnumeratedListing(l, std::move(d), recurse);}}listing d;m_listedDirectories.emplace_back(std::move(d));}on_listed_directory();
}
在上传时,会由do_start_recursive_operation()启动一个执行local_recursive_operation::thread_entry()的线程。这个线程不停从嵌套根recursion_roots_里,拿出来目录里需要访问的目录root.m_dirsToVisit,然后在目录下找文件,找到文件就存文件到d.files.emplace_back(std::move(entry)),找到目录就存目录到d.dirs.emplace_back(std::move(entry))。找到到超过5000个文件,或者扫描结束,则通过EnqueueEnumeratedListing(l, std::move(d), recurse)和on_listed_directory(),把存入的目录入队列root.add_dir_to_visit(localSub, remoteSub)、把存入的文件m_pQueue->QueueFiles(!m_immediate, site_, d)入系统的文件下载处理的队列。这就处理完啦!
void local_recursive_operation::EnqueueEnumeratedListing(fz::scoped_lock& l, listing&& d, bool recurse)
{if (recursion_roots_.empty()) {return;}auto& root = recursion_roots_.front();if (recurse) {// Queue for recursionfor (auto const& entry : d.dirs) {local_recursion_root::new_dir dir;CLocalPath localSub = d.localPath;localSub.AddSegment(entry.name);CServerPath remoteSub = d.remotePath;if (!remoteSub.empty()) {if (m_operationMode == recursive_transfer) {// Non-flatten caseremoteSub.AddSegment(entry.name);}}root.add_dir_to_visit(localSub, remoteSub); // !!!!!!}}m_listedDirectories.emplace_back(std::move(d));// Hand off to GUI threadif (m_listedDirectories.size() == 1) {l.unlock();on_listed_directory();l.lock();}
}
void CLocalRecursiveOperation::on_listed_directory() {CallAfter(&CLocalRecursiveOperation::OnListedDirectory);
}void CLocalRecursiveOperation::OnListedDirectory()
{if (m_operationMode == recursive_none) {return;}bool const queue = m_operationMode == recursive_transfer || m_operationMode == recursive_transfer_flatten;listing d;bool stop = false;int64_t processed = 0;while (processed < 5000) {{fz::scoped_lock l(mutex_);if (m_listedDirectories.empty()) {break;}d = std::move(m_listedDirectories.front());m_listedDirectories.pop_front();}if (d.localPath.empty()) {stop = true;}else {if (queue) {m_pQueue->QueueFiles(!m_immediate, site_, d); // !!!!!!}++m_processedDirectories;processed += d.files.size();state_.NotifyHandlers(STATECHANGE_LOCAL_RECURSION_LISTING, std::wstring(), &d);}}if (queue) {m_pQueue->QueueFile_Finish(m_immediate);}m_processedFiles += processed;if (stop) {StopRecursiveOperation();}else if (processed) {state_.NotifyHandlers(STATECHANGE_LOCAL_RECURSION_STATUS);if (processed >= 5000) {CallAfter(&CLocalRecursiveOperation::OnListedDirectory);}}
}