2

这个问题可能看起来很奇怪,但我并没有拼错:我想解压缩我下载的一些数据而不将它们写在硬盘上。为此,我将它下载到一个动态分配的缓冲区中,并且我想将它发送到我使用的 zlib 包装器(miniunzip)。问题是这个实现很长(2-3K 行),我想避免只为几行重写它。我想知道是否有任何方法可以通过 FILE* 结构读取缓冲区(miniunzip 使用它自己的结构,但我发现加载器下隐藏了一个“fopen()”)。如果可以提供帮助,我知道它的长度。

提前感谢并原谅我糟糕的语法。

我在 Windows 和 UNIX 系统(OSX/GNU Linux)上工作。

4

2 回答 2

4

如果您谈论的是 zlib 中包含的 minizip 库,您可以使用该unzOpen2函数,该函数允许您指定包含要使用的 I/O 函数的结构。这应该让你开始:

struct zmem_data {
  char *buf;
  size_t length;
};

static voidpf zmemopen(voidpf opaque, const char *filename, int mode) {
  if ((mode&ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) return NULL;
  uLong *pos = malloc(sizeof(uLong));
  *pos = 0;
  return pos;
}

static uLong zmemread(voidpf opaque, voidpf stream, void* buf, uLong size) {
  struct zmem_data *data = (struct zmem_data*)opaque;
  uLong *pos = (uLong*)stream;
  uLong remaining = data->length - *pos;
  uLong readlength = size < remaining ? size : remaining;
  if (*pos > data->length) return 0;
  memcpy(buf, data->buf+*pos, readlength);
  *pos += readlength;
  return readlength;
}

static uLong zmemwrite(voidpf opaque, voidpf stream, const void *buf, uLong size) {
  /* no write support for now */
  return 0;
}

static int zmemclose(voidpf opaque, voidpf stream) {
  free(stream);
  return 0;
}

static int zmemerror(voidpf opaque, voidpf stream) {
  if (stream == NULL) return 1;
  else return 0;
}

static long zmemtell(voidpf opaque, voidpf stream) {
  return *(uLong*)stream;
}

static long zmemseek(voidpf opaque, voidpf stream, uLong offset, int origin) {
  struct zmem_data *data = (struct zmem_data*)opaque;
  uLong *pos = (uLong*)stream;
  switch (origin) {
    case ZLIB_FILEFUNC_SEEK_SET:
      *pos = offset;
      break;
    case ZLIB_FILEFUNC_SEEK_CUR:
      *pos = *pos + offset;
      break;
    case ZLIB_FILEFUNC_SEEK_END:
      *pos = data->length + offset;
      break;
    default:
      return -1;
  }
  return 0;
}

static void init_zmemfile(zlib_filefunc_def *inst, char *buf, size_t length) {
  struct zmem_data *data = malloc(sizeof(struct zmem_data));
  data->buf = buf;
  data->length = length;
  inst->opaque = data;
  inst->zopen_file = zmemopen;
  inst->zread_file = zmemread;
  inst->zwrite_file = zmemwrite;
  inst->ztell_file = zmemtell;
  inst->zseek_file = zmemseek;
  inst->zclose_file = zmemclose;
  inst->zerror_file = zmemerror;
}

static void destroy_zmemfile(zlib_filefunc_def *inst) {
  free(inst->opaque);
  inst->opaque = NULL;
}

void example() {
 zlib_filefunc_dec fileops;
 init_zmemfile(&fileops, buffer, buffer_length);
 unzFile zf = unzOpen2(NULL, &fileops);
 /* ... process zip file ... */
 unzClose(zf);
 destroy_zmemfile(&fileops);
}
于 2012-12-19T23:43:42.713 回答
1

总结您的问题:您想为FILE*内存缓冲区提供一个接口。不,你不能那样做。fread()等调用实际上最终会进行处理您没有的打开文件描述符的系统调用。

你把这件事复杂化了。解压缩代码几乎总是在内存中的缓冲区中工作。如果他们有一个文件接口,那肯定只是一个包装器,它处理将文件读入内存然后解压缩(可能以块的形式来节省内存)。您应该能够找到一个解压库,该库调用解压您给它的缓冲区(只是一个指针和一个长度)。

为什么您不想将下载的数据写入硬盘驱动器当然取决于您,但我希望这是好的,而不是坏的。


另一种选择是打开一个内存映射文件,您在下载期间写入该文件,并在解压缩期间读取该文件。可能有一种方法可以指定文件不写入磁盘,但我不确定。此外,这在 windows 和 linux 之间会有很大的不同。

这些可能会有所帮助:

于 2012-12-19T21:59:59.277 回答