Распаковка GZip в JavaScript со смещением

#javascript #c# #gzip

#javascript #c# #gzip

Вопрос:

API, который я пытаюсь использовать, возвращает ответы в кодировке base64. Ответ сначала сжимается с помощью GZip с 4 битами смещения, а затем кодируется в base64. Я попытался проанализировать ответ с помощью JavaScript (pako и zlib), и в обоих случаях это не удалось. В API есть пример кода C # о том, как должна работать декомпрессия ответа, но я действительно не знаю, как я могу преобразовать это в JavaScript. Итак, может ли кто-нибудь помочь мне преобразовать эту функцию в JavaScript или просто дать мне несколько советов о том, как обрабатывать смещение в 4 байта? Я не нашел ничего подходящего в документации библиотек.

 public string Decompress(string value)
{
  byte[] gzBuffer = Convert.FromBase64String(value);
  using (MemoryStream ms = new MemoryStream())
  {
    int msgLength = BitConverter.ToInt32(gzBuffer, 0);
    ms.Write(gzBuffer, 4, gzBuffer.Length - 4);
    byte[] buffer = new byte[msgLength];
    ms.Position = 0;
    using (System.IO.Compression.GZipStream zip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Decompress))
    {
      zip.Read(buffer, 0, buffer.Length);
    }
    return System.Text.Encoding.Unicode.GetString(buffer, 0, buffer.Length);
   }
}
  

Комментарии:

1. Подождите, значит, строка unicode — это gzip’d (что делает ее меньше), затем base64’d (что делает ее больше), а затем возвращается в http-ответе (который вместо этого мог быть gzip’d)? Блин ( osnews.com/story/19266/wtfsm ).

2. zip.Read не обещает фактически считывать байты длины, если бы вы последовали их примеру, вы могли бы создать строку, содержащую кучу конечных нулей.

3. @JeremyLakeman Да, это была моя интерпретация их метода декомпрессии…

Ответ №1:

Я собираюсь использовать fflate (отказ от ответственности, я автор) для этого. Если вы хотите перевести эту функцию строка за строкой:

 // This is ES6 code; if you want better browser compatibility
// use the ES5 variant.
import { gunzipSync, strToU8, strFromU8 } from 'fflate';
const decompress = str => {
  // atob converts Base64 to Latin-1
  // strToU8(str, true) converts Latin-1 to binary
  const bytes = strToU8(atob(str), true);
  // subarray creates a new view on the same memory buffer
  // gunzipSync synchronously decompresses
  // strFromU8 converts decompressed binary to UTF-8
  return strFromU8(gunzipSync(bytes.subarray(4)));
}
  

Если вы не знаете, что такое ES6:

В вашем HTML-файле:

 <script src="https://cdn.jsdelivr.net/npm/fflate/umd/index.js"></script>
  

В вашем JS:

 var decompress = function(str) {
  var bytes = fflate.strToU8(atob(str), true);
  return fflate.strFromU8(fflate.gunzipSync(bytes.subarray(4)));
}
  

Я хотел бы упомянуть, что потоки почти полностью бесполезны, если вы собираетесь накапливаться в строку в конце, поэтому код C # неоптимален. В то же время, поскольку вы используете стандартную библиотеку, это единственный вариант.

Кроме того, я бы настоятельно рекомендовал использовать вариант обратного вызова (т. Е. gunzip Вместо gunzipSync ), если это возможно, потому что он выполняется в отдельном потоке, чтобы избежать зависания браузера.