diff --git a/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java b/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java
index b63d0ac80af5032e9ddcfe47f122ed17718f932e..b01c051fd459c83137ddc7ab69557bda5bdac91b 100644
--- a/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java
+++ b/src/main/java/cz/it4i/qcmp/cache/QuantizationCacheManager.java
@@ -2,13 +2,13 @@ package cz.it4i.qcmp.cache;
 
 import cz.it4i.qcmp.compression.CompressionOptions;
 import cz.it4i.qcmp.data.V3i;
-import cz.it4i.qcmp.fileformat.*;
+import cz.it4i.qcmp.fileformat.IQvcFile;
+import cz.it4i.qcmp.fileformat.SqQvcFile;
+import cz.it4i.qcmp.fileformat.VqQvcFile;
 import cz.it4i.qcmp.quantization.scalar.SQCodebook;
 import cz.it4i.qcmp.quantization.vector.VQCodebook;
 
-import java.io.DataOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -59,62 +59,8 @@ public class QuantizationCacheManager {
         return new File(cacheFolder, cacheFileName);
     }
 
-
-    /**
-     * Create CacheFileHeader for ScalarQuantization cache.
-     *
-     * @param trainFile Image file used for training.
-     * @param codebook  Final SQ codebook.
-     * @return SQ cache file header.
-     */
-    private QvcHeaderV2 createHeaderForSQ(final String trainFile, final SQCodebook codebook) {
-        final QvcHeaderV2 header = new QvcHeaderV2();
-        header.setQuantizationType(QuantizationType.Scalar);
-        header.setCodebookSize(codebook.getCodebookSize());
-        header.setTrainFileName(trainFile);
-        header.setVectorDims(new V3i(0));
-        return header;
-    }
-
-    /**
-     * Find the correct quantization type based on vector dimension.
-     *
-     * @param vectorDims Quantization vector dimensions.
-     * @return Correct QuantizationType.
-     */
-    private QuantizationType getQuantizationTypeFromVectorDimensions(final V3i vectorDims) {
-        if (vectorDims.getX() > 1) {
-            if (vectorDims.getY() == 1 && vectorDims.getZ() == 1) {
-                return QuantizationType.Vector1D;
-            } else if (vectorDims.getY() > 1 && vectorDims.getZ() == 1) {
-                return QuantizationType.Vector2D;
-            } else {
-                return QuantizationType.Vector3D;
-            }
-        } else if (vectorDims.getX() == 1 && vectorDims.getY() > 1 && vectorDims.getZ() == 1) {
-            return QuantizationType.Vector1D;
-        }
-        return QuantizationType.Invalid;
-    }
-
     /**
-     * Create CacheFileHeader for VQ cache.
-     *
-     * @param trainFile Image file used for training.
-     * @param codebook  Final VQ codebook.
-     * @return VQ cache file header.
-     */
-    private QvcHeaderV2 createHeaderForVQ(final String trainFile, final VQCodebook codebook) {
-        final QvcHeaderV2 header = new QvcHeaderV2();
-        header.setQuantizationType(getQuantizationTypeFromVectorDimensions(codebook.getVectorDims()));
-        header.setCodebookSize(codebook.getCodebookSize());
-        header.setTrainFileName(trainFile);
-        header.setVectorDims(codebook.getVectorDims());
-        return header;
-    }
-
-    /**
-     * Save SQ codebook to cache.
+     * Save SQ codebook to disk cache.
      *
      * @param trainFile Image file used for training.
      * @param codebook  SQ codebook.
@@ -122,19 +68,9 @@ public class QuantizationCacheManager {
      * @throws IOException when fails to save the cache file.
      */
     public String saveCodebook(final String trainFile, final SQCodebook codebook) throws IOException {
-        final String fileName = getCacheFilePathForSQ(trainFile, codebook.getCodebookSize()).getAbsolutePath();
-
-        final QvcHeaderV2 header = createHeaderForSQ(new File(trainFile).getName(), codebook);
-        final SqQvcFile cacheFile = new SqQvcFile(header, codebook);
-
-        try (final FileOutputStream fos = new FileOutputStream(fileName, false);
-             final DataOutputStream dos = new DataOutputStream(fos)) {
-
-            cacheFile.writeToStream(dos);
-        } catch (final IOException ex) {
-            throw new IOException("Failed to save SQ QVC file\n" + ex.getMessage());
-        }
-        return fileName;
+        final String path = getCacheFilePathForSQ(trainFile, codebook.getCodebookSize()).getAbsolutePath();
+        QvcFileWriter.writeSqCacheFile(path, trainFile, codebook);
+        return path;
     }
 
 
@@ -147,21 +83,12 @@ public class QuantizationCacheManager {
      * @throws IOException when fails to save the cache file.
      */
     public String saveCodebook(final String trainFile, final VQCodebook codebook) throws IOException {
-        final String fileName = getCacheFilePathForVQ(trainFile,
-                                                      codebook.getCodebookSize(),
-                                                      codebook.getVectorDims()).getAbsolutePath();
+        final String path = getCacheFilePathForVQ(trainFile,
+                                                  codebook.getCodebookSize(),
+                                                  codebook.getVectorDims()).getAbsolutePath();
 
-        final QvcHeaderV2 header = createHeaderForVQ(new File(trainFile).getName(), codebook);
-        final VqQvcFile cacheFile = new VqQvcFile(header, codebook);
-
-        try (final FileOutputStream fos = new FileOutputStream(fileName, false);
-             final DataOutputStream dos = new DataOutputStream(fos)) {
-
-            cacheFile.writeToStream(dos);
-        } catch (final IOException ex) {
-            throw new IOException("Failed to save VQ cache file\n" + ex.getMessage());
-        }
-        return fileName;
+        QvcFileWriter.writeVqCacheFile(path, trainFile, codebook);
+        return path;
     }
 
     /**
diff --git a/src/main/java/cz/it4i/qcmp/cache/QvcFileWriter.java b/src/main/java/cz/it4i/qcmp/cache/QvcFileWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a643f59ec1ac67836a6cec552cb94efbd16bd6dd
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/cache/QvcFileWriter.java
@@ -0,0 +1,111 @@
+package cz.it4i.qcmp.cache;
+
+import cz.it4i.qcmp.data.V3i;
+import cz.it4i.qcmp.fileformat.QuantizationType;
+import cz.it4i.qcmp.fileformat.QvcHeaderV2;
+import cz.it4i.qcmp.fileformat.SqQvcFile;
+import cz.it4i.qcmp.fileformat.VqQvcFile;
+import cz.it4i.qcmp.quantization.scalar.SQCodebook;
+import cz.it4i.qcmp.quantization.vector.VQCodebook;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class QvcFileWriter {
+    /**
+     * Create QVC file header for SqQvc file.
+     *
+     * @param trainFile Image file used for training.
+     * @param codebook  Final SQ codebook.
+     * @return SQ cache file header.
+     */
+    private static QvcHeaderV2 createQvcHeaderForSQ(final String trainFile, final SQCodebook codebook) {
+        final QvcHeaderV2 header = new QvcHeaderV2();
+        header.setQuantizationType(QuantizationType.Scalar);
+        header.setCodebookSize(codebook.getCodebookSize());
+        header.setTrainFileName(trainFile);
+        header.setVectorDims(new V3i(0));
+        return header;
+    }
+
+    /**
+     * Find the correct quantization type based on vector dimension.
+     *
+     * @param vectorDims Quantization vector dimensions.
+     * @return Correct QuantizationType.
+     */
+    private static QuantizationType getQuantizationTypeFromVectorDimensions(final V3i vectorDims) {
+        if (vectorDims.getX() > 1) {
+            if (vectorDims.getY() == 1 && vectorDims.getZ() == 1) {
+                return QuantizationType.Vector1D;
+            } else if (vectorDims.getY() > 1 && vectorDims.getZ() == 1) {
+                return QuantizationType.Vector2D;
+            } else {
+                return QuantizationType.Vector3D;
+            }
+        } else if (vectorDims.getX() == 1 && vectorDims.getY() > 1 && vectorDims.getZ() == 1) {
+            return QuantizationType.Vector1D;
+        }
+        return QuantizationType.Invalid;
+    }
+
+    /**
+     * Create QVC file header for VqQvc file.
+     *
+     * @param trainFile Image file used for training.
+     * @param codebook  Final VQ codebook.
+     * @return VQ cache file header.
+     */
+    private static QvcHeaderV2 createQvcHeaderForVq(final String trainFile, final VQCodebook codebook) {
+        final QvcHeaderV2 header = new QvcHeaderV2();
+        header.setQuantizationType(getQuantizationTypeFromVectorDimensions(codebook.getVectorDims()));
+        header.setCodebookSize(codebook.getCodebookSize());
+        header.setTrainFileName(trainFile);
+        header.setVectorDims(codebook.getVectorDims());
+        return header;
+    }
+
+    /**
+     * Save scalar quantization codebook as QVC file in file specified by path.
+     *
+     * @param path      Cache file path.
+     * @param trainFile Image file used for training.
+     * @param codebook  Scalar quantization codebook.
+     * @throws IOException when fails to write the cache file.
+     */
+    public static void writeSqCacheFile(final String path, final String trainFile, final SQCodebook codebook) throws IOException {
+        final QvcHeaderV2 header = createQvcHeaderForSQ(new File(trainFile).getName(), codebook);
+        final SqQvcFile cacheFile = new SqQvcFile(header, codebook);
+
+        try (final FileOutputStream fos = new FileOutputStream(path, false);
+             final DataOutputStream dos = new DataOutputStream(fos)) {
+
+            cacheFile.writeToStream(dos);
+        } catch (final IOException ex) {
+            throw new IOException("Failed to save SQ QVC file\n" + ex.getMessage());
+        }
+    }
+
+    /**
+     * Save vector quantization codebook as QVC file in file specified by path.
+     *
+     * @param path      Cache file path.
+     * @param trainFile Image file used for training.
+     * @param codebook  Vector quantization codebook.
+     * @throws IOException when fails to save the cache file.
+     */
+    public static void writeVqCacheFile(final String path, final String trainFile, final VQCodebook codebook) throws IOException {
+        final QvcHeaderV2 header = createQvcHeaderForVq(new File(trainFile).getName(), codebook);
+        final VqQvcFile cacheFile = new VqQvcFile(header, codebook);
+
+        try (final FileOutputStream fos = new FileOutputStream(path, false);
+             final DataOutputStream dos = new DataOutputStream(fos)) {
+
+            cacheFile.writeToStream(dos);
+        } catch (final IOException ex) {
+            throw new IOException("Failed to save VQ QVC file\n" + ex.getMessage());
+        }
+    }
+}