Mercurial > hg > orthanc
comparison OrthancServer/ServerJobs/ArchiveJob.cpp @ 2976:cb5d75143da0
Asynchronous generation of ZIP archives and DICOM medias
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 06 Dec 2018 12:23:46 +0100 |
parents | 10c610e80b15 |
children | 4e43e67f8ecf |
comparison
equal
deleted
inserted
replaced
2970:eea66afed0db | 2976:cb5d75143da0 |
---|---|
45 #define snprintf _snprintf | 45 #define snprintf _snprintf |
46 #endif | 46 #endif |
47 | 47 |
48 static const uint64_t MEGA_BYTES = 1024 * 1024; | 48 static const uint64_t MEGA_BYTES = 1024 * 1024; |
49 static const uint64_t GIGA_BYTES = 1024 * 1024 * 1024; | 49 static const uint64_t GIGA_BYTES = 1024 * 1024 * 1024; |
50 static const char* MEDIA_IMAGES_FOLDER = "IMAGES"; | 50 |
51 static const char* const MEDIA_IMAGES_FOLDER = "IMAGES"; | |
52 static const char* const KEY_DESCRIPTION = "Description"; | |
53 static const char* const KEY_INSTANCES_COUNT = "InstancesCount"; | |
54 static const char* const KEY_UNCOMPRESSED_SIZE_MB = "UncompressedSizeMB"; | |
55 | |
51 | 56 |
52 namespace Orthanc | 57 namespace Orthanc |
53 { | 58 { |
54 static bool IsZip64Required(uint64_t uncompressedSize, | 59 static bool IsZip64Required(uint64_t uncompressedSize, |
55 unsigned int countInstances) | 60 unsigned int countInstances) |
789 instancesCount_(0), | 794 instancesCount_(0), |
790 uncompressedSize_(0) | 795 uncompressedSize_(0) |
791 { | 796 { |
792 } | 797 } |
793 | 798 |
799 | |
800 ArchiveJob::~ArchiveJob() | |
801 { | |
802 if (!mediaArchiveId_.empty()) | |
803 { | |
804 context_.GetMediaArchive().Remove(mediaArchiveId_); | |
805 } | |
806 } | |
807 | |
794 | 808 |
795 void ArchiveJob::SetSynchronousTarget(boost::shared_ptr<TemporaryFile>& target) | 809 void ArchiveJob::SetSynchronousTarget(boost::shared_ptr<TemporaryFile>& target) |
796 { | 810 { |
797 if (target.get() == NULL) | 811 if (target.get() == NULL) |
798 { | 812 { |
799 throw OrthancException(ErrorCode_NullPointer); | 813 throw OrthancException(ErrorCode_NullPointer); |
800 } | 814 } |
801 else if (synchronousTarget_.get() != NULL) | 815 else if (writer_.get() != NULL || // Already started |
816 synchronousTarget_.get() != NULL || | |
817 asynchronousTarget_.get() != NULL) | |
802 { | 818 { |
803 throw OrthancException(ErrorCode_BadSequenceOfCalls); | 819 throw OrthancException(ErrorCode_BadSequenceOfCalls); |
804 } | 820 } |
805 else | 821 else |
806 { | 822 { |
807 synchronousTarget_ = target; | 823 synchronousTarget_ = target; |
808 } | 824 } |
809 } | 825 } |
810 | 826 |
811 | 827 |
828 void ArchiveJob::SetDescription(const std::string& description) | |
829 { | |
830 if (writer_.get() != NULL) // Already started | |
831 { | |
832 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
833 } | |
834 else | |
835 { | |
836 description_ = description; | |
837 } | |
838 } | |
839 | |
840 | |
812 void ArchiveJob::AddResource(const std::string& publicId) | 841 void ArchiveJob::AddResource(const std::string& publicId) |
813 { | 842 { |
814 if (writer_.get() != NULL) // Already started | 843 if (writer_.get() != NULL) // Already started |
815 { | 844 { |
816 throw OrthancException(ErrorCode_BadSequenceOfCalls); | 845 throw OrthancException(ErrorCode_BadSequenceOfCalls); |
817 } | 846 } |
818 | 847 else |
819 ResourceIdentifiers resource(context_.GetIndex(), publicId); | 848 { |
820 archive_->Add(context_.GetIndex(), resource); | 849 ResourceIdentifiers resource(context_.GetIndex(), publicId); |
850 archive_->Add(context_.GetIndex(), resource); | |
851 } | |
821 } | 852 } |
822 | 853 |
823 | 854 |
824 void ArchiveJob::Reset() | 855 void ArchiveJob::Reset() |
825 { | 856 { |
828 } | 859 } |
829 | 860 |
830 | 861 |
831 void ArchiveJob::Start() | 862 void ArchiveJob::Start() |
832 { | 863 { |
864 TemporaryFile* target = NULL; | |
865 | |
833 if (synchronousTarget_.get() == NULL) | 866 if (synchronousTarget_.get() == NULL) |
834 { | 867 { |
835 throw OrthancException(ErrorCode_BadSequenceOfCalls); | 868 asynchronousTarget_.reset(new TemporaryFile); |
869 target = asynchronousTarget_.get(); | |
870 } | |
871 else | |
872 { | |
873 target = synchronousTarget_.get(); | |
836 } | 874 } |
837 | 875 |
838 if (writer_.get() != NULL) | 876 if (writer_.get() != NULL) |
839 { | 877 { |
840 throw OrthancException(ErrorCode_BadSequenceOfCalls); | 878 throw OrthancException(ErrorCode_BadSequenceOfCalls); |
841 } | 879 } |
842 | 880 |
843 writer_.reset(new ZipWriterIterator(*synchronousTarget_, context_, *archive_, | 881 writer_.reset(new ZipWriterIterator(*target, context_, *archive_, |
844 isMedia_, enableExtendedSopClass_)); | 882 isMedia_, enableExtendedSopClass_)); |
845 | 883 |
846 instancesCount_ = writer_->GetInstancesCount(); | 884 instancesCount_ = writer_->GetInstancesCount(); |
847 uncompressedSize_ = writer_->GetUncompressedSize(); | 885 uncompressedSize_ = writer_->GetUncompressedSize(); |
848 } | 886 } |
849 | 887 |
888 | |
889 | |
890 namespace | |
891 { | |
892 class DynamicTemporaryFile : public IDynamicObject | |
893 { | |
894 private: | |
895 std::auto_ptr<TemporaryFile> file_; | |
896 | |
897 public: | |
898 DynamicTemporaryFile(TemporaryFile* f) : file_(f) | |
899 { | |
900 if (f == NULL) | |
901 { | |
902 throw OrthancException(ErrorCode_NullPointer); | |
903 } | |
904 } | |
905 | |
906 const TemporaryFile& GetFile() const | |
907 { | |
908 assert(file_.get() != NULL); | |
909 return *file_; | |
910 } | |
911 }; | |
912 } | |
850 | 913 |
914 | |
915 void ArchiveJob::FinalizeTarget() | |
916 { | |
917 writer_.reset(); // Flush all the results | |
918 | |
919 if (asynchronousTarget_.get() != NULL) | |
920 { | |
921 // Asynchronous behavior: Move the resulting file into the media archive | |
922 mediaArchiveId_ = context_.GetMediaArchive().Add( | |
923 new DynamicTemporaryFile(asynchronousTarget_.release())); | |
924 } | |
925 } | |
926 | |
927 | |
851 JobStepResult ArchiveJob::Step() | 928 JobStepResult ArchiveJob::Step() |
852 { | 929 { |
853 assert(writer_.get() != NULL); | 930 assert(writer_.get() != NULL); |
854 | 931 |
855 if (synchronousTarget_.unique()) | 932 if (synchronousTarget_.get() != NULL && |
933 synchronousTarget_.unique()) | |
856 { | 934 { |
857 LOG(WARNING) << "A client has disconnected while creating an archive"; | 935 LOG(WARNING) << "A client has disconnected while creating an archive"; |
858 return JobStepResult::Failure(ErrorCode_NetworkProtocol); | 936 return JobStepResult::Failure(ErrorCode_NetworkProtocol); |
859 } | 937 } |
860 | 938 |
861 if (writer_->GetStepsCount() == 0) | 939 if (writer_->GetStepsCount() == 0) |
862 { | 940 { |
863 writer_.reset(); // Flush all the results | 941 FinalizeTarget(); |
864 return JobStepResult::Success(); | 942 return JobStepResult::Success(); |
865 } | 943 } |
866 else | 944 else |
867 { | 945 { |
868 writer_->RunStep(currentStep_); | 946 writer_->RunStep(currentStep_); |
869 | 947 |
870 currentStep_ ++; | 948 currentStep_ ++; |
871 | 949 |
872 if (currentStep_ == writer_->GetStepsCount()) | 950 if (currentStep_ == writer_->GetStepsCount()) |
873 { | 951 { |
874 writer_.reset(); // Flush all the results | 952 FinalizeTarget(); |
875 return JobStepResult::Success(); | 953 return JobStepResult::Success(); |
876 } | 954 } |
877 else | 955 else |
878 { | 956 { |
879 return JobStepResult::Continue(); | 957 return JobStepResult::Continue(); |
907 { | 985 { |
908 target = "Archive"; | 986 target = "Archive"; |
909 } | 987 } |
910 } | 988 } |
911 | 989 |
912 | 990 |
913 void ArchiveJob::GetPublicContent(Json::Value& value) | 991 void ArchiveJob::GetPublicContent(Json::Value& value) |
914 { | 992 { |
915 value["Description"] = description_; | 993 value = Json::objectValue; |
916 value["InstancesCount"] = instancesCount_; | 994 value[KEY_DESCRIPTION] = description_; |
917 value["UncompressedSizeMB"] = | 995 value[KEY_INSTANCES_COUNT] = instancesCount_; |
996 value[KEY_UNCOMPRESSED_SIZE_MB] = | |
918 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES); | 997 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES); |
919 } | 998 } |
999 | |
1000 | |
1001 bool ArchiveJob::GetOutput(std::string& output, | |
1002 MimeType& mime, | |
1003 const std::string& key) | |
1004 { | |
1005 if (key == "archive" && | |
1006 !mediaArchiveId_.empty()) | |
1007 { | |
1008 SharedArchive::Accessor accessor(context_.GetMediaArchive(), mediaArchiveId_); | |
1009 | |
1010 if (accessor.IsValid()) | |
1011 { | |
1012 const DynamicTemporaryFile& f = dynamic_cast<DynamicTemporaryFile&>(accessor.GetItem()); | |
1013 f.GetFile().Read(output); | |
1014 mime = MimeType_Zip; | |
1015 return true; | |
1016 } | |
1017 else | |
1018 { | |
1019 return false; | |
1020 } | |
1021 } | |
1022 else | |
1023 { | |
1024 return false; | |
1025 } | |
1026 } | |
920 } | 1027 } |