diff OrthancServer/OrthancRestApi/OrthancRestArchive.cpp @ 1121:82567bac5e25

Creation of ZIP archives for media storage, with DICOMDIR
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 05 Sep 2014 14:28:43 +0200
parents 6968356679c0
children 6e7e5ed91c2d
line wrap: on
line diff
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Wed Sep 03 16:49:26 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Fri Sep 05 14:28:43 2014 +0200
@@ -33,6 +33,7 @@
 #include "../PrecompiledHeadersServer.h"
 #include "OrthancRestApi.h"
 
+#include "../DicomDirWriter.h"
 #include "../../Core/Compression/HierarchicalZipWriter.h"
 #include "../../Core/HttpServer/FilesystemHttpSender.h"
 #include "../../Core/Uuid.h"
@@ -54,30 +55,38 @@
   static std::string GetDirectoryNameInArchive(const Json::Value& resource,
                                                ResourceType resourceType)
   {
+    std::string s;
+
     switch (resourceType)
     {
       case ResourceType_Patient:
       {
         std::string p = resource["MainDicomTags"]["PatientID"].asString();
         std::string n = resource["MainDicomTags"]["PatientName"].asString();
-        return p + " " + n;
+        s = p + " " + n;
+        break;
       }
 
       case ResourceType_Study:
       {
-        return resource["MainDicomTags"]["StudyDescription"].asString();
+        s = resource["MainDicomTags"]["StudyDescription"].asString();
+        break;
       }
         
       case ResourceType_Series:
       {
         std::string d = resource["MainDicomTags"]["SeriesDescription"].asString();
         std::string m = resource["MainDicomTags"]["Modality"].asString();
-        return m + " " + d;
+        s = m + " " + d;
+        break;
       }
         
       default:
         throw OrthancException(ErrorCode_InternalError);
     }
+
+    // Get rid of special characters
+    return Toolbox::ConvertToAscii(s);
   }
 
   static bool CreateRootDirectoryInArchive(HierarchicalZipWriter& writer,
@@ -126,13 +135,6 @@
                               const std::string& instancePublicId,
                               const char* filename)
   {
-    Json::Value instance;
-
-    if (!context.GetIndex().LookupResource(instance, instancePublicId, ResourceType_Instance))
-    {
-      return false;
-    }
-
     writer.OpenFile(filename);
 
     std::string dicom;
@@ -233,13 +235,10 @@
     return true;
   }                                 
 
-  template <enum ResourceType resourceType>
-  static void GetArchive(RestApiGetCall& call)
+
+  static bool IsZip64Required(ServerIndex& index,
+                              const std::string& id)
   {
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
-    std::string id = call.GetUriComponent("id", "");
-
     /**
      * Determine whether ZIP64 is required. Original ZIP format can
      * store up to 2GB of data (some implementation supporting up to
@@ -252,8 +251,8 @@
     unsigned int countStudies;
     unsigned int countSeries;
     unsigned int countInstances;
-    context.GetIndex().GetStatistics(compressedSize, uncompressedSize, 
-                                     countStudies, countSeries, countInstances, id);
+    index.GetStatistics(compressedSize, uncompressedSize, 
+                        countStudies, countSeries, countInstances, id);
     const bool isZip64 = (uncompressedSize >= 2 * GIGA_BYTES ||
                           countInstances >= 65535);
 
@@ -261,6 +260,18 @@
               << (uncompressedSize / MEGA_BYTES) << "MB using the "
               << (isZip64 ? "ZIP64" : "ZIP32") << " file format";
 
+    return isZip64;
+  }
+                              
+
+  template <enum ResourceType resourceType>
+  static void GetArchive(RestApiGetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string id = call.GetUriComponent("id", "");
+    bool isZip64 = IsZip64Required(context.GetIndex(), id);
+
     // Create a RAII for the temporary file to manage the ZIP file
     Toolbox::TemporaryFile tmp;
 
@@ -288,10 +299,75 @@
   }
 
 
+  static void GetMediaArchive(RestApiGetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string id = call.GetUriComponent("id", "");
+    bool isZip64 = IsZip64Required(context.GetIndex(), id);
+
+    // Create a RAII for the temporary file to manage the ZIP file
+    Toolbox::TemporaryFile tmp;
+
+    {
+      // Create a ZIP writer
+      HierarchicalZipWriter writer(tmp.GetPath().c_str());
+      writer.SetZip64(isZip64);
+      writer.OpenDirectory("IMAGES");
+
+      // Create the DICOMDIR writer
+      DicomDirWriter dicomDir;
+
+      // Retrieve the list of the instances
+      std::list<std::string> instances;
+      context.GetIndex().GetChildInstances(instances, id);
+
+      size_t pos = 0;
+      for (std::list<std::string>::const_iterator
+             it = instances.begin(); it != instances.end(); it++, pos++)
+      {
+        // "DICOM restricts the filenames on DICOM media to 8
+        // characters (some systems wrongly use 8.3, but this does not
+        // conform to the standard)."
+        std::string filename = "IM" + boost::lexical_cast<std::string>(pos);
+        writer.OpenFile(filename.c_str());
+
+        std::string dicom;
+        context.ReadFile(dicom, *it, FileContentType_Dicom);
+        writer.Write(dicom);
+
+        ParsedDicomFile parsed(dicom);
+        dicomDir.Add("IMAGES", filename, parsed);
+      }
+
+      // Add the DICOMDIR
+      writer.CloseDirectory();
+      writer.OpenFile("DICOMDIR");
+      std::string s;
+      dicomDir.Encode(s);
+      writer.Write(s);
+    }
+
+    // Prepare the sending of the ZIP file
+    FilesystemHttpSender sender(tmp.GetPath().c_str());
+    sender.SetContentType("application/zip");
+    sender.SetDownloadFilename(id + ".zip");
+
+    // Send the ZIP
+    call.GetOutput().AnswerFile(sender);
+
+    // The temporary file is automatically removed thanks to the RAII
+  }
+
+
   void OrthancRestApi::RegisterArchive()
   {
     Register("/patients/{id}/archive", GetArchive<ResourceType_Patient>);
     Register("/studies/{id}/archive", GetArchive<ResourceType_Study>);
     Register("/series/{id}/archive", GetArchive<ResourceType_Series>);
+
+    Register("/patients/{id}/media", GetMediaArchive);
+    Register("/studies/{id}/media", GetMediaArchive);
+    Register("/series/{id}/media", GetMediaArchive);
   }
 }