changeset 646:fb49bf72ac2d

author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 30 Oct 2013 08:58:45 +0100
parents 75af92b18e23
children a9ea5e311ec5
files Core/Compression/HierarchicalZipWriter.h OrthancServer/OrthancRestApi.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h
diffstat 4 files changed, 121 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Compression/HierarchicalZipWriter.h	Tue Oct 29 20:32:24 2013 +0100
+++ b/Core/Compression/HierarchicalZipWriter.h	Wed Oct 30 08:58:45 2013 +0100
@@ -94,6 +94,16 @@
 
     ~HierarchicalZipWriter();
 
+    void SetZip64(bool isZip64)
+    {
+      writer_.SetZip64(isZip64);
+    }
+
+    bool IsZip64() const
+    {
+      return writer_.IsZip64();
+    }
+
     void SetCompressionLevel(uint8_t level)
     {
       writer_.SetCompressionLevel(level);
--- a/OrthancServer/OrthancRestApi.cpp	Tue Oct 29 20:32:24 2013 +0100
+++ b/OrthancServer/OrthancRestApi.cpp	Wed Oct 30 08:58:45 2013 +0100
@@ -46,6 +46,9 @@
 #include <boost/lexical_cast.hpp>
 #include <glog/logging.h>
 
+static const uint64_t MEGA_BYTES = 1024 * 1024;
+static const uint64_t GIGA_BYTES = 1024 * 1024 * 1024;
+
 
 #define RETRIEVE_CONTEXT(call)                          \
   OrthancRestApi& contextApi =                          \
@@ -551,12 +554,12 @@
                               const std::string& publicId,
                               ResourceType resourceType,
                               bool isFirstLevel)
-  {
+  { 
     Json::Value resource;
     if (!context.GetIndex().LookupResource(resource, publicId, resourceType))
     {
       return false;
-    }
+    }    
 
     if (isFirstLevel && 
         !CreateRootDirectoryInArchive(writer, context, resource, resourceType))
@@ -612,14 +615,36 @@
   static void GetArchive(RestApi::GetCall& call)
   {
     RETRIEVE_CONTEXT(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
+     * 4GB of data), and up to 65535 files.
+     * https://en.wikipedia.org/wiki/Zip_(file_format)#ZIP64
+     **/
+
+    uint64_t uncompressedSize;
+    uint64_t compressedSize;
+    unsigned int countStudies;
+    unsigned int countSeries;
+    unsigned int countInstances;
+    context.GetIndex().GetStatistics(compressedSize, uncompressedSize, 
+                                     countStudies, countSeries, countInstances, id);
+    const bool isZip64 = (uncompressedSize >= 2 * GIGA_BYTES ||
+                          countInstances >= 65535);
+
+    LOG(INFO) << "Creating a ZIP file with " << countInstances << " files of size "
+              << (uncompressedSize / MEGA_BYTES) << "MB using the "
+              << (isZip64 ? "ZIP64" : "ZIP32") << " file format";
 
     // Create a RAII for the temporary file to manage the ZIP file
     Toolbox::TemporaryFile tmp;
-    std::string id = call.GetUriComponent("id", "");
 
     {
       // Create a ZIP writer
       HierarchicalZipWriter writer(tmp.GetPath().c_str());
+      writer.SetZip64(isZip64);
 
       // Store the requested resource into the ZIP
       if (!ArchiveInternal(writer, context, id, resourceType, true))
--- a/OrthancServer/ServerIndex.cpp	Tue Oct 29 20:32:24 2013 +0100
+++ b/OrthancServer/ServerIndex.cpp	Wed Oct 30 08:58:45 2013 +0100
@@ -1301,26 +1301,22 @@
   }
 
 
-  void ServerIndex::GetStatistics(Json::Value& target,
-                                  const std::string& publicId)
+  void ServerIndex::GetStatisticsInternal(/* out */ uint64_t& compressedSize, 
+                                          /* out */ uint64_t& uncompressedSize, 
+                                          /* out */ unsigned int& countStudies, 
+                                          /* out */ unsigned int& countSeries, 
+                                          /* out */ unsigned int& countInstances, 
+                                          /* in  */ int64_t id,
+                                          /* in  */ ResourceType type)
   {
-    boost::mutex::scoped_lock lock(mutex_);
+    std::stack<int64_t> toExplore;
+    toExplore.push(id);
 
-    ResourceType type;
-    int64_t top;
-    if (!db_->LookupResource(publicId, top, type))
-    {
-      throw OrthancException(ErrorCode_UnknownResource);
-    }
-
-    std::stack<int64_t> toExplore;
-    toExplore.push(top);
-
-    int countInstances = 0;
-    int countSeries = 0;
-    int countStudies = 0;
-    uint64_t compressedSize = 0;
-    uint64_t uncompressedSize = 0;
+    countInstances = 0;
+    countSeries = 0;
+    countStudies = 0;
+    compressedSize = 0;
+    uncompressedSize = 0;
 
     while (!toExplore.empty())
     {
@@ -1375,6 +1371,39 @@
       }
     }
 
+    if (countStudies == 0)
+    {
+      countStudies = 1;
+    }
+
+    if (countSeries == 0)
+    {
+      countSeries = 1;
+    }
+  }
+
+
+
+  void ServerIndex::GetStatistics(Json::Value& target,
+                                  const std::string& publicId)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    ResourceType type;
+    int64_t top;
+    if (!db_->LookupResource(publicId, top, type))
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+
+    uint64_t uncompressedSize;
+    uint64_t compressedSize;
+    unsigned int countStudies;
+    unsigned int countSeries;
+    unsigned int countInstances;
+    GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, 
+                          countSeries, countInstances, top, type);
+
     target = Json::objectValue;
     target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize);
     target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES);
@@ -1400,6 +1429,27 @@
   }
 
 
+  void ServerIndex::GetStatistics(/* out */ uint64_t& compressedSize, 
+                                  /* out */ uint64_t& uncompressedSize, 
+                                  /* out */ unsigned int& countStudies, 
+                                  /* out */ unsigned int& countSeries, 
+                                  /* out */ unsigned int& countInstances, 
+                                  const std::string& publicId)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    ResourceType type;
+    int64_t top;
+    if (!db_->LookupResource(publicId, top, type))
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+
+    GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, 
+                          countSeries, countInstances, top, type);    
+  }
+
+
   void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that)
   {
     int stableAge = GetGlobalIntegerParameter("StableAge", 60);
--- a/OrthancServer/ServerIndex.h	Tue Oct 29 20:32:24 2013 +0100
+++ b/OrthancServer/ServerIndex.h	Wed Oct 30 08:58:45 2013 +0100
@@ -90,6 +90,14 @@
     void MarkAsUnstable(int64_t id,
                         Orthanc::ResourceType type);
 
+    void GetStatisticsInternal(/* out */ uint64_t& compressedSize, 
+                               /* out */ uint64_t& uncompressedSize, 
+                               /* out */ unsigned int& countStudies, 
+                               /* out */ unsigned int& countSeries, 
+                               /* out */ unsigned int& countInstances, 
+                               /* in  */ int64_t id,
+                               /* in  */ ResourceType type);
+
   public:
     typedef std::list<FileInfo> Attachments;
 
@@ -187,6 +195,13 @@
     void GetStatistics(Json::Value& target,
                        const std::string& publicId);
 
+    void GetStatistics(/* out */ uint64_t& compressedSize, 
+                       /* out */ uint64_t& uncompressedSize, 
+                       /* out */ unsigned int& countStudies, 
+                       /* out */ unsigned int& countSeries, 
+                       /* out */ unsigned int& countInstances, 
+                       const std::string& publicId);
+
     void LookupTagValue(std::list<std::string>& result,
                         DicomTag tag,
                         const std::string& value,