changeset 4357:886bc367aeb2

"/instances" can be used to import ZIP archives provided in the POST body
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 08 Dec 2020 13:17:40 +0100
parents 18c94a82f3d4
children d6929f052ec4
files NEWS OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp
diffstat 2 files changed, 83 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Dec 08 12:55:32 2020 +0100
+++ b/NEWS	Tue Dec 08 13:17:40 2020 +0100
@@ -2,6 +2,12 @@
 ===============================
 
 
+REST API
+--------
+
+* "/instances" can be used to import ZIP archives provided in the POST body
+
+
 Version 1.8.1 (2020-12-07)
 ==========================
 
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Tue Dec 08 12:55:32 2020 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp	Tue Dec 08 13:17:40 2020 +0100
@@ -35,6 +35,7 @@
 #include "OrthancRestApi.h"
 
 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h"
+#include "../../../OrthancFramework/Sources/Compression/ZipReader.h"
 #include "../../../OrthancFramework/Sources/Logging.h"
 #include "../../../OrthancFramework/Sources/MetricsRegistry.h"
 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
@@ -61,18 +62,26 @@
   }
 
 
+  static void SetupResourceAnswer(Json::Value& result,
+                                  DicomInstanceToStore& instance,
+                                  StoreStatus status,
+                                  const std::string& instanceId)
+  {
+    SetupResourceAnswer(result, instanceId, ResourceType_Instance, status);
+
+    result["ParentPatient"] = instance.GetHasher().HashPatient();
+    result["ParentStudy"] = instance.GetHasher().HashStudy();
+    result["ParentSeries"] = instance.GetHasher().HashSeries();
+  }
+
+
   void OrthancRestApi::AnswerStoredInstance(RestApiPostCall& call,
                                             DicomInstanceToStore& instance,
                                             StoreStatus status,
                                             const std::string& instanceId) const
   {
     Json::Value result;
-    SetupResourceAnswer(result, instanceId, ResourceType_Instance, status);
-
-    result["ParentPatient"] = instance.GetHasher().HashPatient();
-    result["ParentStudy"] = instance.GetHasher().HashStudy();
-    result["ParentSeries"] = instance.GetHasher().HashSeries();
-
+    SetupResourceAnswer(result, instance, status, instanceId);
     call.GetOutput().AnswerJson(result);
   }
 
@@ -121,28 +130,75 @@
                              "Received an empty DICOM file");
     }
 
-    // The lifetime of "dicom" must be longer than "toStore", as the
-    // latter can possibly store a reference to the former (*)
-    std::string dicom;
+    if (ZipReader::IsZipMemoryBuffer(call.GetBodyData(), call.GetBodySize()))
+    {
+      // New in Orthanc 1.9.0
+      std::unique_ptr<ZipReader> reader(ZipReader::CreateFromMemory(call.GetBodyData(), call.GetBodySize()));
+
+      Json::Value answer = Json::arrayValue;
+      
+      std::string filename, content;
+      while (reader->ReadNextFile(filename, content))
+      {
+        if (!content.empty())
+        {
+          LOG(INFO) << "Uploading DICOM file from ZIP archive: " << filename;
+          
+          DicomInstanceToStore toStore;
+          toStore.SetOrigin(DicomInstanceOrigin::FromRest(call));
+          toStore.SetBuffer(content.c_str(), content.size());
+
+          std::string publicId;
 
-    DicomInstanceToStore toStore;
-    toStore.SetOrigin(DicomInstanceOrigin::FromRest(call));
+          try
+          {
+            StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default);
 
-    if (boost::iequals(call.GetHttpHeader("content-encoding", ""), "gzip"))
-    {
-      GzipCompressor compressor;
-      compressor.Uncompress(dicom, call.GetBodyData(), call.GetBodySize());
-      toStore.SetBuffer(dicom.c_str(), dicom.size());  // (*)
+            Json::Value info;
+            SetupResourceAnswer(info, toStore, status, publicId);
+            answer.append(info);
+          }
+          catch (OrthancException& e)
+          {
+            if (e.GetErrorCode() == ErrorCode_BadFileFormat)
+            {
+              LOG(ERROR) << "Cannot import non-DICOM file from ZIP archive: " << filename;
+            }
+            else
+            {
+              throw;
+            }
+          }
+        }
+      }      
+
+      call.GetOutput().AnswerJson(answer);
     }
     else
     {
-      toStore.SetBuffer(call.GetBodyData(), call.GetBodySize());
-    }    
+      // The lifetime of "dicom" must be longer than "toStore", as the
+      // latter can possibly store a reference to the former (*)
+      std::string dicom;
+
+      DicomInstanceToStore toStore;
+      toStore.SetOrigin(DicomInstanceOrigin::FromRest(call));
 
-    std::string publicId;
-    StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default);
+      if (boost::iequals(call.GetHttpHeader("content-encoding", ""), "gzip"))
+      {
+        GzipCompressor compressor;
+        compressor.Uncompress(dicom, call.GetBodyData(), call.GetBodySize());
+        toStore.SetBuffer(dicom.c_str(), dicom.size());  // (*)
+      }
+      else
+      {
+        toStore.SetBuffer(call.GetBodyData(), call.GetBodySize());
+      }    
 
-    OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, publicId);
+      std::string publicId;
+      StoreStatus status = context.Store(publicId, toStore, StoreInstanceMode_Default);
+
+      OrthancRestApi::GetApi(call).AnswerStoredInstance(call, toStore, status, publicId);
+    }
   }