Mercurial > hg > orthanc
changeset 4239:c8754c4c1862
upload DICOM using WebDAV
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 09 Oct 2020 11:38:03 +0200 |
parents | c007fb7c8395 |
children | 799c0c527ced |
files | OrthancFramework/Sources/HttpServer/WebDavStorage.cpp OrthancFramework/Sources/HttpServer/WebDavStorage.h OrthancFramework/Sources/SystemToolbox.cpp OrthancServer/Sources/main.cpp |
diffstat | 4 files changed, 199 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancFramework/Sources/HttpServer/WebDavStorage.cpp Fri Oct 09 10:28:34 2020 +0200 +++ b/OrthancFramework/Sources/HttpServer/WebDavStorage.cpp Fri Oct 09 11:38:03 2020 +0200 @@ -160,6 +160,11 @@ } } + size_t GetSize() const + { + return files_.size() + subfolders_.size(); + } + const boost::posix_time::ptime& GetModificationTime() const { return time_; @@ -327,6 +332,33 @@ } } } + + + void RemoveEmptyFolders() + { + std::list<std::string> emptyFolders; + + for (Subfolders::const_iterator it = subfolders_.begin(); it != subfolders_.end(); ++it) + { + assert(it->second != NULL); + it->second->RemoveEmptyFolders(); + + if (it->second->GetSize() == 0) + { + assert(it->second != NULL); + delete it->second; + + emptyFolders.push_back(it->first); + } + } + + for (std::list<std::string>::const_iterator it = emptyFolders.begin(); + it != emptyFolders.end(); ++it) + { + assert(subfolders_.find(*it) != subfolders_.end()); + subfolders_.erase(*it); + } + } }; @@ -459,4 +491,11 @@ return root_->DeleteItem(path); } } + + + void WebDavStorage::RemoveEmptyFolders() + { + boost::recursive_mutex::scoped_lock lock(mutex_); + root_->RemoveEmptyFolders(); + } }
--- a/OrthancFramework/Sources/HttpServer/WebDavStorage.h Fri Oct 09 10:28:34 2020 +0200 +++ b/OrthancFramework/Sources/HttpServer/WebDavStorage.h Fri Oct 09 11:38:03 2020 +0200 @@ -68,5 +68,7 @@ virtual void Stop() ORTHANC_OVERRIDE { } + + void RemoveEmptyFolders(); }; }
--- a/OrthancFramework/Sources/SystemToolbox.cpp Fri Oct 09 10:28:34 2020 +0200 +++ b/OrthancFramework/Sources/SystemToolbox.cpp Fri Oct 09 11:38:03 2020 +0200 @@ -700,6 +700,10 @@ } // Images types + else if (extension == ".dcm") + { + return MimeType_Dicom; + } else if (extension == ".jpg" || extension == ".jpeg") {
--- a/OrthancServer/Sources/main.cpp Fri Oct 09 10:28:34 2020 +0200 +++ b/OrthancServer/Sources/main.cpp Fri Oct 09 11:38:03 2020 +0200 @@ -781,6 +781,7 @@ static const char* const BY_STUDIES = "by-studies"; static const char* const BY_DATE = "by-dates"; static const char* const BY_UIDS = "by-uids"; +static const char* const UPLOADS = "uploads"; static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; class DummyBucket2 : public IWebDavBucket // TODO @@ -789,6 +790,12 @@ typedef std::map<ResourceType, std::string> Templates; + static boost::posix_time::ptime GetNow() + { + return boost::posix_time::second_clock::universal_time(); + } + + static void LookupTime(boost::posix_time::ptime& target, ServerContext& context, const std::string& publicId, @@ -807,7 +814,7 @@ } } - target = boost::posix_time::second_clock::universal_time(); // Now + target = GetNow(); } @@ -1838,6 +1845,62 @@ } }; + + static void UploadWorker(DummyBucket2* that) + { + assert(that != NULL); + + boost::posix_time::ptime lastModification = GetNow(); + + while (that->running_) + { + std::unique_ptr<IDynamicObject> obj(that->uploadQueue_.Dequeue(100)); + if (obj.get() != NULL) + { + that->Upload(reinterpret_cast<const SingleValueObject<std::string>&>(*obj).GetValue()); + lastModification = GetNow(); + } + else if (GetNow() - lastModification > boost::posix_time::seconds(10)) + { + // After every 10 seconds of inactivity, remove the empty folders + LOG(INFO) << "Cleaning up the empty WebDAV upload folders"; + that->uploads_.RemoveEmptyFolders(); + lastModification = GetNow(); + } + } + } + + void Upload(const std::string& path) + { + UriComponents uri; + Toolbox::SplitUriComponents(uri, path); + + LOG(INFO) << "Upload from WebDAV: " << path; + + MimeType mime; + std::string content; + boost::posix_time::ptime time; + if (uploads_.GetFileContent(mime, content, time, uri)) + { + DicomInstanceToStore instance; + // instance.SetOrigin(DicomInstanceOrigin_WebDav); + instance.SetBuffer(content.c_str(), content.size()); + + std::string publicId; + StoreStatus status = context_.Store(publicId, instance, StoreInstanceMode_Default); + if (status == StoreStatus_Success || + status == StoreStatus_AlreadyStored) + { + LOG(INFO) << "Successfully imported DICOM instance from WebDAV: " << path << " (Orthanc ID: " << publicId << ")"; + uploads_.DeleteItem(uri); + } + else + { + LOG(WARNING) << "Cannot import DICOM instance from WebWAV: " << path; + } + } + } + ServerContext& context_; std::unique_ptr<INode> patients_; @@ -1845,11 +1908,16 @@ std::unique_ptr<INode> dates_; Templates patientsTemplates_; Templates studiesTemplates_; + WebDavStorage uploads_; + SharedMessageQueue uploadQueue_; + boost::thread uploadThread_; + bool running_; - public: DummyBucket2(ServerContext& context) : - context_(context) + context_(context), + uploads_(false /* store uploads as temporary files */), + running_(false) { patientsTemplates_[ResourceType_Patient] = "{{PatientID}} - {{PatientName}}"; patientsTemplates_[ResourceType_Study] = "{{StudyDate}} - {{StudyDescription}}"; @@ -1863,6 +1931,11 @@ dates_.reset(new ListOfStudiesByYear(context, studiesTemplates_)); } + virtual ~DummyBucket2() + { + Stop(); + } + virtual bool IsExistingFolder(const UriComponents& path) ORTHANC_OVERRIDE { if (path.empty()) @@ -1889,6 +1962,10 @@ IWebDavBucket::Collection tmp; return dates_->ListCollection(tmp, UriComponents(path.begin() + 1, path.end())); } + else if (path[0] == UPLOADS) + { + return uploads_.IsExistingFolder(UriComponents(path.begin() + 1, path.end())); + } else { return false; @@ -1904,6 +1981,7 @@ collection.AddResource(new Folder(BY_PATIENTS)); collection.AddResource(new Folder(BY_STUDIES)); collection.AddResource(new Folder(BY_UIDS)); + collection.AddResource(new Folder(UPLOADS)); return true; } else if (path[0] == BY_UIDS) @@ -1957,6 +2035,10 @@ { return dates_->ListCollection(collection, UriComponents(path.begin() + 1, path.end())); } + else if (path[0] == UPLOADS) + { + return uploads_.ListCollection(collection, UriComponents(path.begin() + 1, path.end())); + } else { return false; @@ -2022,6 +2104,10 @@ mime = MimeType_Dicom; return visitor.IsSuccess(); } + else + { + return false; + } } else if (path[0] == BY_PATIENTS) { @@ -2031,39 +2117,96 @@ { return studies_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); } - else if (path[0] == BY_DATE) + else if (path[0] == UPLOADS) { - return dates_->GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); + return uploads_.GetFileContent(mime, content, modificationTime, UriComponents(path.begin() + 1, path.end())); } - - return false; + else + { + return false; + } } virtual bool StoreFile(const std::string& content, const UriComponents& path) ORTHANC_OVERRIDE { - return false; + if (path.size() >= 1 && + path[0] == UPLOADS) + { + UriComponents subpath(UriComponents(path.begin() + 1, path.end())); + + if (uploads_.StoreFile(content, subpath)) + { + if (!content.empty()) + { + uploadQueue_.Enqueue(new SingleValueObject<std::string>(Toolbox::FlattenUri(subpath))); + } + return true; + } + else + { + return false; + } + } + else + { + return false; + } } virtual bool CreateFolder(const UriComponents& path) { - return false; + if (path.size() >= 1 && + path[0] == UPLOADS) + { + return uploads_.CreateFolder(UriComponents(path.begin() + 1, path.end())); + } + else + { + return false; + } } virtual bool DeleteItem(const std::vector<std::string>& path) ORTHANC_OVERRIDE { - LOG(WARNING) << "DELETE: " << Toolbox::FlattenUri(path); - return false; // read-only + if (path.size() >= 1 && + path[0] == UPLOADS) + { + return uploads_.DeleteItem(UriComponents(path.begin() + 1, path.end())); + } + else + { + return false; // read-only + } } virtual void Start() ORTHANC_OVERRIDE { + if (running_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + LOG(INFO) << "Starting the WebDAV upload thread"; + running_ = true; + uploadThread_ = boost::thread(UploadWorker, this); + } } virtual void Stop() ORTHANC_OVERRIDE { + if (running_) + { + LOG(INFO) << "Stopping the WebDAV upload thread"; + running_ = false; + if (uploadThread_.joinable()) + { + uploadThread_.join(); + } + } } };