changeset 6026:4db83cdab1bc attach-custom-data

simplification by merging CreateInstance() and CreateAttachment() in IStorageArea
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 24 Feb 2025 17:19:33 +0100
parents a1d661a598c8
children 83deef099c75
files OrthancFramework/Sources/FileStorage/IStorageArea.h OrthancFramework/Sources/FileStorage/MemoryStorageArea.h OrthancFramework/Sources/FileStorage/StorageAccessor.cpp OrthancFramework/Sources/FileStorage/StorageAccessor.h OrthancFramework/UnitTestsSources/FileStorageTests.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/PluginsEnumerations.cpp OrthancServer/Plugins/Engine/PluginsEnumerations.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Sources/ServerContext.cpp
diffstat 10 files changed, 150 insertions(+), 330 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/FileStorage/IStorageArea.h	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/IStorageArea.h	Mon Feb 24 17:19:33 2025 +0100
@@ -43,22 +43,13 @@
     {
     }
 
-    virtual void CreateInstance(std::string& customData,
-                               const DicomInstanceToStore& instance,
-                               const std::string& uuid,
-                               const void* content,
-                               size_t size,
-                               FileContentType type,
-                               bool isCompressed) = 0;
-
-    virtual void CreateAttachment(std::string& customData,
-                                  const std::string& resourceId,
-                                  ResourceType resourceLevel,
-                                  const std::string& uuid,
-                                  const void* content,
-                                  size_t size,
-                                  FileContentType type,
-                                  bool isCompressed) = 0;
+    virtual void Create(std::string& customData /* out */,
+                        const std::string& uuid,
+                        const void* content,
+                        size_t size,
+                        FileContentType type,
+                        CompressionType compression,
+                        const DicomInstanceToStore* dicomInstance /* can be NULL if not a DICOM instance */) = 0;
 
     virtual IMemoryBuffer* ReadWhole(const std::string& uuid,
                                      FileContentType type,
@@ -81,32 +72,21 @@
   class ICoreStorageArea : public IStorageArea
   {
   public:
-    virtual void CreateInstance(std::string& customData,
-                               const DicomInstanceToStore& instance,
-                               const std::string& uuid,
-                               const void* content,
-                               size_t size,
-                               FileContentType type,
-                               bool isCompressed) ORTHANC_OVERRIDE
+    virtual void Create(std::string& customData,
+                        const std::string& uuid,
+                        const void* content,
+                        size_t size,
+                        FileContentType type,
+                        CompressionType compression,
+                        const DicomInstanceToStore* dicomInstance) ORTHANC_OVERRIDE
     {
-      Create(uuid, content, size, type);
-    }
-
-    virtual void CreateAttachment(std::string& customData,
-                                  const std::string& resourceId,
-                                  ResourceType resourceLevel,
-                                  const std::string& uuid,
-                                  const void* content,
-                                  size_t size,
-                                  FileContentType type,
-                                  bool isCompressed) ORTHANC_OVERRIDE
-    {
+      customData.clear();
       Create(uuid, content, size, type);
     }
 
     virtual IMemoryBuffer* ReadWhole(const std::string& uuid,
                                      FileContentType type,
-                                     const std::string& /*customData*/) ORTHANC_OVERRIDE
+                                     const std::string& customData) ORTHANC_OVERRIDE
     {
       return Read(uuid, type);
     }
@@ -115,7 +95,7 @@
                                      FileContentType type,
                                      uint64_t start /* inclusive */,
                                      uint64_t end /* exclusive */,
-                                     const std::string& /*customData */) ORTHANC_OVERRIDE
+                                     const std::string& customData) ORTHANC_OVERRIDE
     {
       return ReadRange(uuid, type, start, end);
     }
@@ -128,7 +108,7 @@
     }
 
     virtual void Create(const std::string& uuid,
-                        const void* content, 
+                        const void* content,
                         size_t size,
                         FileContentType type) = 0;
 
@@ -142,6 +122,5 @@
 
     virtual void Remove(const std::string& uuid,
                         FileContentType type) = 0;
-
   };
 }
--- a/OrthancFramework/Sources/FileStorage/MemoryStorageArea.h	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/MemoryStorageArea.h	Mon Feb 24 17:19:33 2025 +0100
@@ -44,7 +44,6 @@
   public:
     virtual ~MemoryStorageArea();
     
-  protected:
     virtual void Create(const std::string& uuid,
                         const void* content,
                         size_t size,
--- a/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp	Mon Feb 24 17:19:33 2025 +0100
@@ -310,15 +310,15 @@
   }
 
 
-  FileInfo StorageAccessor::WriteInstance(std::string& customData,
-                                          const DicomInstanceToStore& instance,
-                                          const void* data,
-                                          size_t size,
-                                          FileContentType type,
-                                          CompressionType compression,
-                                          bool storeMd5,
-                                          const std::string& uuid)
+  FileInfo StorageAccessor::Write(const void* data,
+                                  size_t size,
+                                  FileContentType type,
+                                  CompressionType compression,
+                                  bool storeMd5,
+                                  const DicomInstanceToStore* instance)
   {
+    const std::string uuid = Toolbox::GenerateUuid();
+
     std::string md5;
 
     if (storeMd5)
@@ -326,13 +326,15 @@
       Toolbox::ComputeMD5(md5, data, size);
     }
 
+    std::string customData;
+
     switch (compression)
     {
       case CompressionType_None:
       {
         {
           MetricsTimer timer(*this, METRICS_CREATE_DURATION);
-          area_.CreateInstance(customData, instance, uuid, data, size, type, false);
+          area_.Create(customData, uuid, data, size, type, compression, instance);
         }
 
         if (metrics_ != NULL)
@@ -368,11 +370,11 @@
 
           if (compressed.size() > 0)
           {
-            area_.CreateInstance(customData, instance, uuid, &compressed[0], compressed.size(), type, true);
+            area_.Create(customData, uuid, &compressed[0], compressed.size(), type, compression, instance);
           }
           else
           {
-            area_.CreateInstance(customData, instance, uuid, NULL, 0, type, true);
+            area_.Create(customData, uuid, NULL, 0, type, compression, instance);
           }
         }
 
@@ -396,95 +398,6 @@
     }
   }
 
-  FileInfo StorageAccessor::WriteAttachment(std::string& customData,
-                                            const std::string& resourceId,
-                                            ResourceType resourceType,
-                                            const void* data,
-                                            size_t size,
-                                            FileContentType type,
-                                            CompressionType compression,
-                                            bool storeMd5,
-                                            const std::string& uuid)
-  {
-    std::string md5;
-
-    if (storeMd5)
-    {
-      Toolbox::ComputeMD5(md5, data, size);
-    }
-
-    switch (compression)
-    {
-      case CompressionType_None:
-      {
-        {
-          MetricsTimer timer(*this, METRICS_CREATE_DURATION);
-          area_.CreateAttachment(customData, resourceId, resourceType, uuid, data, size, type, false);
-        }
-        
-        if (metrics_ != NULL)
-        {
-          metrics_->IncrementIntegerValue(METRICS_WRITTEN_BYTES, size);
-        }
-        
-        if (cache_ != NULL)
-        {
-          StorageCache::Accessor cacheAccessor(*cache_);
-          cacheAccessor.Add(uuid, type, data, size);
-        }
-
-
-        return FileInfo(uuid, type, size, md5, customData);
-      }
-
-      case CompressionType_ZlibWithSize:
-      {
-        ZlibCompressor zlib;
-
-        std::string compressed;
-        zlib.Compress(compressed, data, size);
-
-        std::string compressedMD5;
-      
-        if (storeMd5)
-        {
-          Toolbox::ComputeMD5(compressedMD5, compressed);
-        }
-
-        {
-          MetricsTimer timer(*this, METRICS_CREATE_DURATION);
-
-          if (compressed.size() > 0)
-          {
-            area_.CreateAttachment(customData, resourceId, resourceType, uuid, &compressed[0], compressed.size(), type, true);
-          }
-          else
-          {
-            area_.CreateAttachment(customData, resourceId, resourceType, uuid, NULL, 0, type, true);
-          }
-        }
-
-        if (metrics_ != NULL)
-        {
-          metrics_->IncrementIntegerValue(METRICS_WRITTEN_BYTES, compressed.size());
-        }
-
-        if (cache_ != NULL)
-        {
-          StorageCache::Accessor cacheAccessor(*cache_);
-          cacheAccessor.Add(uuid, type, data, size);    // always add uncompressed data to cache
-        }
-
-        return FileInfo(uuid, type, size, md5,
-                        CompressionType_ZlibWithSize, compressed.size(), compressedMD5, customData);
-      }
-
-      default:
-        throw OrthancException(ErrorCode_NotImplemented);
-    }
-  }
-
-
   void StorageAccessor::Read(std::string& content,
                              const FileInfo& info)
   {
--- a/OrthancFramework/Sources/FileStorage/StorageAccessor.h	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.h	Mon Feb 24 17:19:33 2025 +0100
@@ -133,24 +133,12 @@
                     StorageCache& cache,
                     MetricsRegistry& metrics);
 
-    FileInfo WriteInstance(std::string& customData,
-                           const DicomInstanceToStore& instance,
-                           const void* data,
-                           size_t size,
-                           FileContentType type,
-                           CompressionType compression,
-                           bool storeMd5,
-                           const std::string& uuid);
-
-    FileInfo WriteAttachment(std::string& customData,
-                             const std::string& resourceId,
-                             ResourceType resourceType,
-                             const void* data,
-                             size_t size,
-                             FileContentType type,
-                             CompressionType compression,
-                             bool storeMd5,
-                             const std::string& uuid);
+    FileInfo Write(const void* data,
+                   size_t size,
+                   FileContentType type,
+                   CompressionType compression,
+                   bool storeMd5,
+                   const DicomInstanceToStore* instance);
 
     void Read(std::string& content,
               const FileInfo& info);
@@ -195,8 +183,6 @@
                     const std::string& contentFilename);
 #endif
 
-    bool HandlesCustomData();
-
   private:
     void ReadStartRangeInternal(std::string& target,
                                 const FileInfo& info,
--- a/OrthancFramework/UnitTestsSources/FileStorageTests.cpp	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancFramework/UnitTestsSources/FileStorageTests.cpp	Mon Feb 24 17:19:33 2025 +0100
@@ -173,10 +173,9 @@
   StorageCache cache;
   StorageAccessor accessor(s, cache);
 
-  std::string data = "Hello world";
-  std::string uuid = Toolbox::GenerateUuid();
-  FileInfo info = accessor.WriteAttachment(data, "", ResourceType_Instance, data.c_str(), data.size(), FileContentType_Dicom, CompressionType_None, true, uuid);
-  
+  const std::string data = "Hello world";
+  FileInfo info = accessor.Write(data.c_str(), data.size(), FileContentType_Dicom, CompressionType_None, true, NULL);
+
   std::string r;
   accessor.Read(r, info);
 
@@ -196,10 +195,9 @@
   StorageCache cache;
   StorageAccessor accessor(s, cache);
 
-  std::string data = "Hello world";
-  std::string uuid = Toolbox::GenerateUuid();
-  FileInfo info = accessor.WriteAttachment(data, "", ResourceType_Instance, data.c_str(), data.size(), FileContentType_Dicom, CompressionType_ZlibWithSize, true, uuid);
-  
+  const std::string data = "Hello world";
+  FileInfo info = accessor.Write(data.c_str(), data.size(), FileContentType_Dicom, CompressionType_ZlibWithSize, true, NULL);
+
   std::string r;
   accessor.Read(r, info);
 
@@ -209,7 +207,6 @@
   ASSERT_EQ(FileContentType_Dicom, info.GetContentType());
   ASSERT_EQ("3e25960a79dbc69b674cd4ec67a72c62", info.GetUncompressedMD5());
   ASSERT_NE(info.GetUncompressedMD5(), info.GetCompressedMD5());
-  ASSERT_EQ(uuid, info.GetUuid());
 }
 
 
@@ -219,13 +216,13 @@
   StorageCache cache;
   StorageAccessor accessor(s, cache);
 
-  std::string r;
-  std::string compressedData = "Hello";
-  std::string uncompressedData = "HelloWorld";
+  const std::string compressedData = "Hello";
+  const std::string uncompressedData = "HelloWorld";
 
-  FileInfo compressedInfo = accessor.WriteAttachment(compressedData, "", ResourceType_Instance, compressedData.c_str(), compressedData.size(), FileContentType_Dicom, CompressionType_ZlibWithSize, false, Toolbox::GenerateUuid());
-  FileInfo uncompressedInfo = accessor.WriteAttachment(uncompressedData, "", ResourceType_Instance, uncompressedData.c_str(), uncompressedData.size(), FileContentType_Dicom, CompressionType_None, false, Toolbox::GenerateUuid());
+  FileInfo compressedInfo = accessor.Write(compressedData.c_str(), compressedData.size(), FileContentType_Dicom, CompressionType_ZlibWithSize, false, NULL);
+  FileInfo uncompressedInfo = accessor.Write(uncompressedData.c_str(), uncompressedData.size(), FileContentType_Dicom, CompressionType_None, false, NULL);
 
+  std::string r;
   accessor.Read(r, compressedInfo);
   ASSERT_EQ(compressedData, r);
 
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Mon Feb 24 17:19:33 2025 +0100
@@ -933,11 +933,10 @@
     class PluginStorageArea3 : public IStorageArea
     {
     private:
-      OrthancPluginStorageCreateInstance createInstance_;
-      OrthancPluginStorageCreateAttachment createAttachment_;
-      OrthancPluginStorageRemove2 remove2_;
+      OrthancPluginStorageCreate2     create_;
       OrthancPluginStorageReadWhole2  readWhole2_;
       OrthancPluginStorageReadRange2  readRange2_;
+      OrthancPluginStorageRemove2     remove2_;
 
       PluginsErrorDictionary&    errorDictionary_;
 
@@ -990,39 +989,42 @@
       
     public:
       PluginStorageArea3(const _OrthancPluginRegisterStorageArea3& callbacks,
-                      PluginsErrorDictionary&  errorDictionary) : 
-        createInstance_(callbacks.createInstance),
-        createAttachment_(callbacks.createAttachment),
-        remove2_(callbacks.remove),
+                         PluginsErrorDictionary&  errorDictionary) :
+        create_(callbacks.create),
         readWhole2_(callbacks.readWhole),
         readRange2_(callbacks.readRange),
+        remove2_(callbacks.remove),
         errorDictionary_(errorDictionary)
       {
-        if (createInstance_ == NULL ||
-            createAttachment_ == NULL ||
-            remove2_ == NULL ||
-            readWhole2_ == NULL)
-        {
-          throw OrthancException(ErrorCode_Plugin, "Storage area plugin doesn't implement all the required primitives (createInstance, createAttachment, remove, readWhole");
-        }
-      }
-
-      virtual void CreateInstance(std::string& customData,
-                                const DicomInstanceToStore& instance,
-                                const std::string& uuid,
-                                const void* content,
-                                size_t size,
-                                FileContentType type,
-                                bool isCompressed) ORTHANC_OVERRIDE
+        if (create_ == NULL ||
+            readWhole2_ == NULL ||
+            remove2_ == NULL)
+        {
+          throw OrthancException(ErrorCode_Plugin, "Storage area plugin doesn't implement all the required primitives (createInstance, remove, readWhole");
+        }
+      }
+
+      virtual void Create(std::string& customData /* out */,
+                          const std::string& uuid,
+                          const void* content,
+                          size_t size,
+                          FileContentType type,
+                          CompressionType compression,
+                          const DicomInstanceToStore* dicomInstance /* can be NULL if not a DICOM instance */) ORTHANC_OVERRIDE
       {
         OrthancPluginMemoryBuffer customDataBuffer;
-        Orthanc::OrthancPlugins::DicomInstanceFromCallback wrapped(instance);
-
-        OrthancPluginErrorCode error = createInstance_(&customDataBuffer,
-                                                       uuid.c_str(),
-                                                       reinterpret_cast<OrthancPluginDicomInstance*>(&wrapped),
-                                                       content, size, Plugins::Convert(type),
-                                                       isCompressed);
+        OrthancPluginErrorCode error;
+
+        if (dicomInstance != NULL)
+        {
+          Orthanc::OrthancPlugins::DicomInstanceFromCallback wrapped(*dicomInstance);
+          error = create_(&customDataBuffer, uuid.c_str(), content, size, Plugins::Convert(type), Plugins::Convert(compression),
+                          reinterpret_cast<OrthancPluginDicomInstance*>(&wrapped));
+        }
+        else
+        {
+          error = create_(&customDataBuffer, uuid.c_str(), content, size, Plugins::Convert(type), Plugins::Convert(compression), NULL);
+        }
 
         if (error != OrthancPluginErrorCode_Success)
         {
@@ -1032,38 +1034,7 @@
 
         if (customDataBuffer.size > 0)
         {
-          customData.assign(reinterpret_cast<char*>(customDataBuffer.data), 
-                            static_cast<size_t>(customDataBuffer.size));
-        }
-      }
-
-      virtual void CreateAttachment(std::string& customData,
-                                    const std::string& resourceId,
-                                    ResourceType resourceLevel,
-                                    const std::string& uuid,
-                                    const void* content,
-                                    size_t size,
-                                    FileContentType type,
-                                    bool isCompressed) ORTHANC_OVERRIDE
-      {
-        OrthancPluginMemoryBuffer customDataBuffer;
-
-        OrthancPluginErrorCode error = createAttachment_(&customDataBuffer,
-                                                         uuid.c_str(),
-                                                         resourceId.c_str(),
-                                                         Plugins::Convert(resourceLevel),
-                                                         content, size, Plugins::Convert(type),
-                                                         isCompressed);
-
-        if (error != OrthancPluginErrorCode_Success)
-        {
-          errorDictionary_.LogError(error, true);
-          throw OrthancException(static_cast<ErrorCode>(error));
-        }
-
-        if (customDataBuffer.size > 0)
-        {
-          customData.assign(reinterpret_cast<char*>(customDataBuffer.data), 
+          customData.assign(reinterpret_cast<char*>(customDataBuffer.data),
                             static_cast<size_t>(customDataBuffer.size));
         }
       }
@@ -1072,8 +1043,8 @@
                           FileContentType type,
                           const std::string& customData) ORTHANC_OVERRIDE
       {
-        OrthancPluginErrorCode error = remove2_
-          (uuid.c_str(), customData.c_str(), Plugins::Convert(type));
+        OrthancPluginErrorCode error = remove2_(uuid.c_str(), Plugins::Convert(type),
+                                                customData.empty() ? NULL : customData.c_str(), customData.size());
 
         if (error != OrthancPluginErrorCode_Success)
         {
@@ -1083,8 +1054,8 @@
       }
 
       virtual IMemoryBuffer* ReadWhole(const std::string& uuid,
-                                  FileContentType type,
-                                  const std::string& customData) ORTHANC_OVERRIDE
+                                       FileContentType type,
+                                       const std::string& customData) ORTHANC_OVERRIDE
       {
         std::unique_ptr<MallocMemoryBuffer> result(new MallocMemoryBuffer);
 
@@ -1092,7 +1063,8 @@
         buffer.size = 0;
         buffer.data = NULL;
         
-        OrthancPluginErrorCode error = readWhole2_(&buffer, uuid.c_str(), customData.c_str(), Plugins::Convert(type));
+        OrthancPluginErrorCode error = readWhole2_(&buffer, uuid.c_str(), Plugins::Convert(type),
+                                                   customData.empty() ? NULL : customData.c_str(), customData.size());
 
         if (error == OrthancPluginErrorCode_Success)
         {
@@ -1137,7 +1109,7 @@
             buffer.size = static_cast<uint64_t>(range.size());
 
             OrthancPluginErrorCode error =
-              readRange2_(&buffer, uuid.c_str(), customData.c_str(), Plugins::Convert(type), start);
+              readRange2_(&buffer, uuid.c_str(), Plugins::Convert(type), start, customData.empty() ? NULL : customData.c_str(), customData.size());
 
             if (error == OrthancPluginErrorCode_Success)
             {
@@ -3862,6 +3834,12 @@
           break;
         }
 
+        case OrthancPluginCompressionType_None:
+        {
+          CopyToMemoryBuffer(*p.target, p.source, p.size);
+          return;
+        }
+
         default:
           throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
--- a/OrthancServer/Plugins/Engine/PluginsEnumerations.cpp	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancServer/Plugins/Engine/PluginsEnumerations.cpp	Mon Feb 24 17:19:33 2025 +0100
@@ -664,5 +664,21 @@
           throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
     }
+
+
+    OrthancPluginCompressionType Convert(CompressionType type)
+    {
+      switch (type)
+      {
+        case CompressionType_None:
+          return OrthancPluginCompressionType_None;
+
+        case CompressionType_ZlibWithSize:
+          return OrthancPluginCompressionType_ZlibWithSize;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
   }
 }
--- a/OrthancServer/Plugins/Engine/PluginsEnumerations.h	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancServer/Plugins/Engine/PluginsEnumerations.h	Mon Feb 24 17:19:33 2025 +0100
@@ -73,6 +73,8 @@
     ResourceType Convert(OrthancPluginResourceType type);
 
     OrthancPluginConstraintType Convert(ConstraintType constraint);
+
+    OrthancPluginCompressionType Convert(CompressionType type);
   }
 }
 
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Feb 24 17:19:33 2025 +0100
@@ -16,7 +16,7 @@
  *    - Register all its REST callbacks using ::OrthancPluginRegisterRestCallback().
  *    - Possibly register its callback for received DICOM instances using ::OrthancPluginRegisterOnStoredInstanceCallback().
  *    - Possibly register its callback for changes to the DICOM store using ::OrthancPluginRegisterOnChangeCallback().
- *    - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea2().
+ *    - Possibly register a custom storage area using ::OrthancPluginRegisterStorageArea3().
  *    - Possibly register a custom database back-end area using OrthancPluginRegisterDatabaseBackendV4().
  *    - Possibly register a handler for C-Find SCP using OrthancPluginRegisterFindCallback().
  *    - Possibly register a handler for C-Find SCP against DICOM worklists using OrthancPluginRegisterWorklistCallback().
@@ -792,6 +792,7 @@
     OrthancPluginCompressionType_ZlibWithSize = 1,  /*!< zlib, prefixed with uncompressed size (uint64_t) */
     OrthancPluginCompressionType_Gzip = 2,          /*!< Standard gzip compression */
     OrthancPluginCompressionType_GzipWithSize = 3,  /*!< gzip, prefixed with uncompressed size (uint64_t) */
+    OrthancPluginCompressionType_None = 4,          /*!< No compression (new in Orthanc 1.12.7) */
 
     _OrthancPluginCompressionType_INTERNAL = 0x7fffffff
   } OrthancPluginCompressionType;
@@ -1368,8 +1369,7 @@
    * @param type The content type corresponding to this file. 
    * @return 0 if success, other value if error.
    * @ingroup Callbacks
-   * @deprecated New plugins should use OrthancPluginStorageReadWhole2 and OrthancPluginStorageReadRange2
-   * 
+   *
    * @warning The "content" buffer *must* have been allocated using
    * the "malloc()" function of your C standard library (i.e. nor
    * "new[]", neither a pointer to a buffer). The "free()" function of
@@ -1443,54 +1443,31 @@
 
 
 
-
-  /**
-   * @brief Callback for writing to the storage area.
-   *
-   * Signature of a callback function that is triggered when Orthanc writes an instance to the storage area.
-   *
-   * @param customData The custom data of the attachment (out)
-   * @param uuid The UUID of the file.
-   * @param instance The DICOM instance being stored.
-   * @param content The content of the file (might be compressed data, hence the need for the DICOM instance arg to access tags).
-   * @param size The size of the file.
-   * @param type The content type corresponding to this file.
-   * @return 0 if success, other value if error.
-   * @ingroup Callbacks
-   **/
-  typedef OrthancPluginErrorCode (*OrthancPluginStorageCreateInstance) (
-    OrthancPluginMemoryBuffer* customData,
-    const char* uuid,
-    const OrthancPluginDicomInstance*  instance,
-    const void* content,
-    int64_t size,
-    OrthancPluginContentType type,
-    uint8_t isCompressed);
-
   /**
    * @brief Callback for writing to the storage area.
    *
    * Signature of a callback function that is triggered when Orthanc writes a file to the storage area.
    *
-   * @param customData The custom data of the attachment (out)
+   * @param customData Custom, plugin-specific data associated with the attachment (out).
+   * It must be allocated by the plugin using OrthancPluginCreateMemoryBuffer64(). The core of Orthanc will free it.
    * @param uuid The UUID of the file.
-   * @param resourceId The resource ID the file is attached to.
-   * @param resourceType The resource Type the file is attached to.
-   * @param content The content of the file (might be compressed data, hence the need for the DICOM instance arg to access tags).
+   * @param content The content of the file (might be compressed data).
    * @param size The size of the file.
    * @param type The content type corresponding to this file.
+   * @param compressionType The compression algorithm used to encode `content` (the absence of compression
+   * is indicated using `OrthancPluginCompressionType_None`).
+   * @param dicomInstance The DICOM instance being stored. Equals `NULL` if not storing a DICOM instance.
    * @return 0 if success, other value if error.
    * @ingroup Callbacks
    **/
-  typedef OrthancPluginErrorCode (*OrthancPluginStorageCreateAttachment) (
+  typedef OrthancPluginErrorCode (*OrthancPluginStorageCreate2) (
     OrthancPluginMemoryBuffer* customData,
     const char* uuid,
-    const char* resourceId,
-    OrthancPluginResourceType resourceType,
     const void* content,
-    int64_t size,
+    uint64_t size,
     OrthancPluginContentType type,
-    uint8_t isCompressed);
+    OrthancPluginCompressionType compressionType,
+    const OrthancPluginDicomInstance* dicomInstance);
 
 
 
@@ -1510,8 +1487,9 @@
   typedef OrthancPluginErrorCode (*OrthancPluginStorageReadWhole2) (
     OrthancPluginMemoryBuffer64* target,
     const char* uuid,
-    const char* customData,
-    OrthancPluginContentType type);
+    OrthancPluginContentType type,
+    const void* customData,
+    uint64_t customDataSize);
 
 
 
@@ -1535,9 +1513,10 @@
   typedef OrthancPluginErrorCode (*OrthancPluginStorageReadRange2) (
     OrthancPluginMemoryBuffer64* target,
     const char* uuid,
-    const char* customData,
     OrthancPluginContentType type,
-    uint64_t rangeStart);
+    uint64_t rangeStart,
+    const void* customData,
+    uint64_t customDataSize);
 
 
 
@@ -1554,8 +1533,9 @@
    **/
   typedef OrthancPluginErrorCode (*OrthancPluginStorageRemove2) (
     const char* uuid,
-    const char* customData,
-    OrthancPluginContentType type);
+    OrthancPluginContentType type,
+    const void* customData,
+    uint64_t customDataSize);
 
 
   /**
@@ -3439,7 +3419,7 @@
    * @param read The callback function to read a file from the custom storage area.
    * @param remove The callback function to remove a file from the custom storage area.
    * @ingroup Callbacks
-   * @deprecated Please use OrthancPluginRegisterStorageArea2()
+   * @deprecated New plugins should use OrthancPluginRegisterStorageArea3()
    **/
   ORTHANC_PLUGIN_DEPRECATED ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea(
     OrthancPluginContext*       context,
@@ -9029,6 +9009,7 @@
    * If this feature is not supported by the plugin, this value can be set to NULL.
    * @param remove The callback function to remove a file from the custom storage area.
    * @ingroup Callbacks
+   * @deprecated New plugins should use OrthancPluginRegisterStorageArea3()
    **/
   ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea2(
     OrthancPluginContext*          context,
@@ -9482,11 +9463,10 @@
 
   typedef struct
   {
-    OrthancPluginStorageCreateInstance    createInstance;
-    OrthancPluginStorageCreateAttachment  createAttachment;
-    OrthancPluginStorageReadWhole2        readWhole;
-    OrthancPluginStorageReadRange2        readRange;
-    OrthancPluginStorageRemove2           remove;
+    OrthancPluginStorageCreate2     create;
+    OrthancPluginStorageReadWhole2  readWhole;
+    OrthancPluginStorageReadRange2  readRange;
+    OrthancPluginStorageRemove2     remove;
   } _OrthancPluginRegisterStorageArea3;
 
   /**
@@ -9506,16 +9486,14 @@
    * @ingroup Callbacks
    **/
   ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageArea3(
-    OrthancPluginContext*                 context,
-    OrthancPluginStorageCreateInstance    createInstance,
-    OrthancPluginStorageCreateAttachment  createAttachement,
-    OrthancPluginStorageReadWhole2        readWhole,
-    OrthancPluginStorageReadRange2        readRange,
-    OrthancPluginStorageRemove2           remove)
+    OrthancPluginContext*           context,
+    OrthancPluginStorageCreate2     create,
+    OrthancPluginStorageReadWhole2  readWhole,
+    OrthancPluginStorageReadRange2  readRange,
+    OrthancPluginStorageRemove2     remove)
   {
     _OrthancPluginRegisterStorageArea3 params;
-    params.createAttachment = createAttachement;
-    params.createInstance = createInstance;
+    params.create = create;
     params.readWhole = readWhole;
     params.readRange = readRange;
     params.remove = remove;
--- a/OrthancServer/Sources/ServerContext.cpp	Mon Feb 24 15:32:38 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.cpp	Mon Feb 24 17:19:33 2025 +0100
@@ -708,11 +708,7 @@
       // TODO Should we use "gzip" instead?
       CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
 
-      std::string dicomCustomData;
-      std::string dicomUuid = Toolbox::GenerateUuid();
-
-      FileInfo dicomInfo = accessor.WriteInstance(dicomCustomData, dicom, dicom.GetBufferData(), dicom.GetBufferSize(), 
-                                          FileContentType_Dicom, compression, storeMD5_, dicomUuid);
+      FileInfo dicomInfo = accessor.Write(dicom.GetBufferData(), dicom.GetBufferSize(), FileContentType_Dicom, compression, storeMD5_, &dicom);
 
       ServerIndex::Attachments attachments;
       attachments.push_back(dicomInfo);
@@ -722,11 +718,7 @@
           (!area_.HasReadRange() ||
            compressionEnabled_))
       {
-        std::string dicomHeaderCustomData;
-        std::string dicomHeaderUuid = Toolbox::GenerateUuid();
-
-        dicomUntilPixelData = accessor.WriteInstance(dicomHeaderCustomData, dicom, dicom.GetBufferData(), pixelDataOffset, 
-                                             FileContentType_DicomUntilPixelData, compression, storeMD5_, dicomHeaderUuid);
+        dicomUntilPixelData = accessor.Write(dicom.GetBufferData(), pixelDataOffset, FileContentType_DicomUntilPixelData, compression, storeMD5_, NULL);
         attachments.push_back(dicomUntilPixelData);
       }
 
@@ -1025,24 +1017,7 @@
     StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
     accessor.Read(content, attachment);
 
-    std::string newUuid = Toolbox::GenerateUuid();
-    std::string newCustomData;
-    FileInfo modified;
-
-    // if (attachmentType == FileContentType_Dicom || attachmentType == FileContentType_DicomUntilPixelData)
-    // {
-    //   // DicomInstanceToStore instance;
-    //   // TODO_CUSTOM_DATA: get the Instance such that we can call accessor.GetCustomData ...
-    //   // modified = accessor.WriteInstance(newCustomData, instance, content.empty() ? NULL : content.c_str(),
-    //   //                                 content.size(), attachmentType, compression, storeMD5_, newUuid);
-    // }
-    // else
-    {
-      modified = accessor.WriteAttachment(newCustomData, resourceId, level, content.empty() ? NULL : content.c_str(),
-                                          content.size(), attachmentType, compression, storeMD5_, newUuid);
-    }
-
-
+    FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(), content.size(), attachmentType, compression, storeMD5_, NULL);
 
     try
     {
@@ -1551,12 +1526,9 @@
 
     StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
 
-    std::string uuid = Toolbox::GenerateUuid();
-    std::string customData;
-
     assert(attachmentType != FileContentType_Dicom && attachmentType != FileContentType_DicomUntilPixelData); // this method can not be used to store instances
 
-    FileInfo attachment = accessor.WriteAttachment(customData, resourceId, resourceType, data, size, attachmentType, compression, storeMD5_, uuid);
+    FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_, NULL);
 
     try
     {