changeset 81:0ec5e2e327b1

zip writer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 24 Sep 2012 10:33:41 +0200
parents 6212bf978584
children 9eb40cad7935
files CMakeLists.txt Core/Compression/ZipWriter.cpp Core/Compression/ZipWriter.h Core/Compression/ZlibCompressor.cpp Resources/CMake/BoostConfiguration.cmake Resources/CMake/ZlibConfiguration.cmake UnitTests/Zip.cpp
diffstat 7 files changed, 246 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Sep 20 15:18:12 2012 +0200
+++ b/CMakeLists.txt	Mon Sep 24 10:33:41 2012 +0200
@@ -133,6 +133,7 @@
   Core/ChunkedBuffer.cpp
   Core/Compression/BufferCompressor.cpp
   Core/Compression/ZlibCompressor.cpp
+  Core/Compression/ZipWriter.cpp
   Core/OrthancException.cpp
   Core/DicomFormat/DicomArray.cpp
   Core/DicomFormat/DicomMap.cpp
@@ -187,6 +188,7 @@
   UnitTests/SQLite.cpp
   UnitTests/SQLiteChromium.cpp
   UnitTests/Versions.cpp
+  UnitTests/Zip.cpp
   )
 
 TARGET_LINK_LIBRARIES(Orthanc ServerLibrary CoreLibrary)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/ZipWriter.cpp	Mon Sep 24 10:33:41 2012 +0200
@@ -0,0 +1,160 @@
+#include "ZipWriter.h"
+
+#include <contrib/minizip/zip.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "../OrthancException.h"
+
+
+static void PrepareFileInfo(zip_fileinfo& zfi)
+{
+  memset(&zfi, 0, sizeof(zfi));
+
+  using namespace boost::posix_time;
+  ptime now = second_clock::local_time();
+
+  boost::gregorian::date today = now.date();
+  ptime midnight(today);
+
+  time_duration sinceMidnight = now - midnight;
+  zfi.tmz_date.tm_sec = sinceMidnight.seconds();  // seconds after the minute - [0,59]
+  zfi.tmz_date.tm_min = sinceMidnight.minutes();  // minutes after the hour - [0,59]
+  zfi.tmz_date.tm_hour = sinceMidnight.hours();  // hours since midnight - [0,23]
+
+  // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_day.html
+  zfi.tmz_date.tm_mday = today.day();  // day of the month - [1,31]
+
+  // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_month.html
+  zfi.tmz_date.tm_mon = today.month() - 1;  // months since January - [0,11]
+
+  // http://www.boost.org/doc/libs/1_35_0/doc/html/boost/gregorian/greg_year.html
+  zfi.tmz_date.tm_year = today.year();  // years - [1980..2044]
+}
+
+
+
+namespace Orthanc
+{
+  struct ZipWriter::PImpl
+  {
+    zipFile file_;
+  };
+
+  ZipWriter::ZipWriter() : pimpl_(new PImpl)
+  {
+    compressionLevel_ = 6;
+    hasFileInZip_ = false;
+
+    pimpl_->file_ = NULL;
+  }
+
+  ZipWriter::~ZipWriter()
+  {
+    Close();
+  }
+
+  void ZipWriter::Close()
+  {
+    if (IsOpen())
+    {
+      zipClose(pimpl_->file_, "Created by Orthanc");
+      pimpl_->file_ = NULL;
+      hasFileInZip_ = false;
+    }
+  }
+
+  bool ZipWriter::IsOpen() const
+  {
+    return pimpl_->file_ != NULL;
+  }
+
+  void ZipWriter::Open()
+  {
+    if (IsOpen())
+    {
+      return;
+    }
+
+    if (path_.size() == 0)
+    {
+      throw OrthancException("Please call SetOutputPath() before creating the file");
+    }
+
+    hasFileInZip_ = false;
+    pimpl_->file_ = zipOpen64(path_.c_str(), APPEND_STATUS_CREATE);
+    if (!pimpl_->file_)
+    {
+      throw OrthancException(ErrorCode_CannotWriteFile);
+    }
+  }
+
+  void ZipWriter::SetOutputPath(const char* path)
+  {
+    Close();
+    path_ = path;
+  }
+
+  void ZipWriter::SetCompressionLevel(uint8_t level)
+  {
+    if (level >= 10)
+    {
+      throw OrthancException("ZIP compression level must be between 0 (no compression) and 9 (highest compression");
+    }
+
+    compressionLevel_ = level;
+  }
+
+  void ZipWriter::CreateFileInZip(const char* path)
+  {
+    Open();
+
+    zip_fileinfo zfi;
+    PrepareFileInfo(zfi);
+
+    if (zipOpenNewFileInZip64(pimpl_->file_, path,
+                              &zfi,
+                              NULL,   0,
+                              NULL,   0,
+                              "",  // Comment
+                              Z_DEFLATED,
+                              compressionLevel_, 1) != 0)
+    {
+      throw OrthancException(ErrorCode_CannotWriteFile);
+    }
+
+    hasFileInZip_ = true;
+  }
+
+
+  void ZipWriter::Write(const std::string& data)
+  {
+    if (data.size())
+    {
+      Write(&data[0], data.size());
+    }
+  }
+
+
+  void ZipWriter::Write(const char* data, size_t length)
+  {
+    if (!hasFileInZip_)
+    {
+      throw OrthancException("Call first CreateFileInZip()");
+    }
+
+    const size_t maxBytesInAStep = std::numeric_limits<int32_t>::max();
+
+    while (length > 0)
+    {
+      int bytes = static_cast<int32_t>(length <= maxBytesInAStep ? length : maxBytesInAStep);
+
+      if (zipWriteInFileInZip(pimpl_->file_, data, bytes))
+      {
+        throw OrthancException(ErrorCode_CannotWriteFile);
+      }
+      
+      data += bytes;
+      length -= bytes;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Compression/ZipWriter.h	Mon Sep 24 10:33:41 2012 +0200
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+#include <boost/shared_ptr.hpp>
+
+namespace Orthanc
+{
+  class ZipWriter
+  {
+  private:
+    struct PImpl;
+    boost::shared_ptr<PImpl> pimpl_;
+
+    bool hasFileInZip_;
+    uint8_t compressionLevel_;
+    std::string path_;
+
+  public:
+    ZipWriter();
+
+    ~ZipWriter();
+
+    void SetCompressionLevel(uint8_t level);
+
+    uint8_t GetCompressionLevel() const
+    {
+      return compressionLevel_;
+    }
+    
+    void Open();
+
+    void Close();
+
+    bool IsOpen() const;
+
+    void SetOutputPath(const char* path);
+
+    const std::string& GetOutputPath() const
+    {
+      return path_;
+    }
+
+    void CreateFileInZip(const char* path);
+
+    void Write(const char* data, size_t length);
+
+    void Write(const std::string& data);
+  };
+}
--- a/Core/Compression/ZlibCompressor.cpp	Thu Sep 20 15:18:12 2012 +0200
+++ b/Core/Compression/ZlibCompressor.cpp	Mon Sep 24 10:33:41 2012 +0200
@@ -33,6 +33,8 @@
     {
       throw OrthancException("Zlib compression level must be between 0 (no compression) and 9 (highest compression");
     }
+
+    compressionLevel_ = level;
   }
 
 
--- a/Resources/CMake/BoostConfiguration.cmake	Thu Sep 20 15:18:12 2012 +0200
+++ b/Resources/CMake/BoostConfiguration.cmake	Mon Sep 24 10:33:41 2012 +0200
@@ -24,7 +24,7 @@
 if (BOOST_STATIC)
   SET(BOOST_NAME boost_1_49_0)
   SET(BOOST_SOURCES_DIR ${CMAKE_BINARY_DIR}/${BOOST_NAME})
-  DownloadPackage("http://switch.dl.sourceforge.net/project/boost/boost/1.49.0/${BOOST_NAME}.tar.gz" "${BOOST_SOURCES_DIR}" "${BOOST_PRELOADED}" "${BOOST_NAME}/boost ${BOOST_NAME}/libs/thread/src ${BOOST_NAME}/libs/system/src ${BOOST_NAME}/libs/filesystem/v3/src ${BOOST_NAME}/libs/locale/src")
+  DownloadPackage("http://switch.dl.sourceforge.net/project/boost/boost/1.49.0/${BOOST_NAME}.tar.gz" "${BOOST_SOURCES_DIR}" "${BOOST_PRELOADED}" "${BOOST_NAME}/boost ${BOOST_NAME}/libs/thread/src ${BOOST_NAME}/libs/system/src ${BOOST_NAME}/libs/filesystem/v3/src ${BOOST_NAME}/libs/locale/src ${BOOST_NAME}/libs/date_time/src")
 
   set(BOOST_SOURCES)
   if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@@ -50,12 +50,13 @@
   endif()
 
   list(APPEND BOOST_SOURCES
-    ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
+    ${BOOST_SOURCES_DIR}/libs/date_time/src/gregorian/greg_month.cpp
+    ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/codecvt_error_category.cpp
+    ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/operations.cpp
     ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/path.cpp
     ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/path_traits.cpp
-    ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/operations.cpp
-    ${BOOST_SOURCES_DIR}/libs/filesystem/v3/src/codecvt_error_category.cpp
     ${BOOST_SOURCES_DIR}/libs/locale/src/encoding/codepage.cpp
+    ${BOOST_SOURCES_DIR}/libs/system/src/error_code.cpp
     )
 
   list(APPEND THIRD_PARTY_SOURCES ${BOOST_SOURCES})
--- a/Resources/CMake/ZlibConfiguration.cmake	Thu Sep 20 15:18:12 2012 +0200
+++ b/Resources/CMake/ZlibConfiguration.cmake	Mon Sep 24 10:33:41 2012 +0200
@@ -1,4 +1,4 @@
-if (${STATIC_BUILD})
+if (ON) #(${STATIC_BUILD})
   SET(ZLIB_SOURCES_DIR ${CMAKE_BINARY_DIR}/zlib-1.2.7)
   DownloadPackage("http://zlib.net/zlib-1.2.7.tar.gz" "${ZLIB_SOURCES_DIR}" "${ZLIB_PRELOADED}" "")
 
@@ -22,6 +22,8 @@
     ${ZLIB_SOURCES_DIR}/trees.c 
     ${ZLIB_SOURCES_DIR}/uncompr.c 
     ${ZLIB_SOURCES_DIR}/zutil.c
+    ${ZLIB_SOURCES_DIR}/contrib/minizip/ioapi.c
+    ${ZLIB_SOURCES_DIR}/contrib/minizip/zip.c
     )
 
   source_group(ThirdParty\\ZLib REGULAR_EXPRESSION ${ZLIB_SOURCES_DIR}/.*)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTests/Zip.cpp	Mon Sep 24 10:33:41 2012 +0200
@@ -0,0 +1,24 @@
+#include "gtest/gtest.h"
+
+#include "../Core/OrthancException.h"
+#include "../Core/Compression/ZipWriter.h"
+
+
+TEST(ZipWriter, Basic)
+{
+  Orthanc::ZipWriter w;
+  w.SetOutputPath("hello.zip");
+  w.Open();
+  w.CreateFileInZip("world/hello");
+  w.Write("Hello world");
+}
+
+
+TEST(ZipWriter, Exceptions)
+{
+  Orthanc::ZipWriter w;
+  ASSERT_THROW(w.Open(), Orthanc::OrthancException);
+  w.SetOutputPath("hello.zip");
+  w.Open();
+  ASSERT_THROW(w.Write("hello world"), Orthanc::OrthancException);
+}