Skip to content

Add Raw Write Mode #67

Description

@nickbabcock

Currently, rawzip requires all zip entry data to be written as uncompressed bytes, with the library automatically calculating CRC32 checksums and tracking uncompressed size. This forces users to decompress and recompress data when transcoding or modifying only a subset of entries in existing zip files.

Prior art:

  • Go's archive/zip package provides CreateRaw() and OpenRaw() methods that allow direct writing of already-compressed data without decompression/recompression.
  • Similarly, the zip crate has ZipWriter::raw_copy_file that copies an existing zip entry with decompression

My preference would be something more akin to Go's implementation as if one already has compressed data (and it's not from a zip file), then the zip crate seems too limiting.

The API could be something like:

  impl<W: Write> ZipArchiveWriter<W> {
      // Maybe even name: AsRef<[u8]> to allow what otherwise would be invalid names
      pub fn new_raw_file(&mut self, name: &str) -> ZipRawFileBuilder<'_, W> {
      }
  }

  pub struct ZipRawFileBuilder<'a, W> {
      archive: &'a mut ZipArchiveWriter<W>,
      name: &'a str,
      compression_method: CompressionMethod,
      modification_time: Option<UtcDateTime>,
      unix_permissions: Option<u32>,
  }

  impl<'a, W> ZipRawFileBuilder<'a, W> {
      pub fn compression_method(mut self, method: CompressionMethod) -> Self {}
      pub fn last_modified(mut self, modification_time: UtcDateTime) -> Self {}
      pub fn unix_permissions(mut self, permissions: u32) -> Self {}
      pub fn create(self) -> Result<ZipRawEntryWriter<'a, W>, Error> { }
  }

  pub struct ZipRawEntryWriter<'a, W> {
      inner: &'a mut ZipArchiveWriter<W>,
      compressed_bytes: u64,
  }

  impl<W: Write> ZipRawEntryWriter<'_, W> {
      pub fn get_mut(&mut self) -> &mut ZipArchiveWriter<W> {
          self.inner
      }

      pub fn finish(self, output: DataDescriptorOutput) -> Result<u64, Error> {
          // Write data descriptor with provided metadata
          // Add entry to central directory
          // Return compressed bytes written
      }
  }

  impl<W: Write> Write for ZipRawEntryWriter<'_, W> {
      fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
          let bytes_written = self.inner.writer.write(buf)?;
          self.compressed_bytes += bytes_written as u64;
          Ok(bytes_written)
      }

      fn flush(&mut self) -> io::Result<()> {
          self.inner.writer.flush()
      }
  }

I could see a use case for having a bit-for-bit identical copy of local file headers and central directory record (which this won't do)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions