Файл поврежден при загрузке с Controller Action - PullRequest
1 голос
/ 16 мая 2019

Я генерирую некоторый контент JSON, а затем выполняю GZipping этот контент перед возвратом gzipped контента пользователю из действия контроллера MVC.

Генерация контента и gzipping работает правильно, какЯ могу вывести сгенерированный файл на диск, а затем я могу открыть этот файл с помощью GZip.Однако при возврате содержимого в браузер содержимое было повреждено.

Я пробовал несколько разных подходов к возврату содержимого в браузер, например

return File(byte[], "application/gzip");

return new FileStreamResult(stream, "application/gzip")

. А такжезапись непосредственно в Response с использованием методов BinaryWrite () и WriteFile ()

Независимо от того, что я делаю, файл, который я получаю в браузере, поврежден.

Этот код показывает способ, которым яВ настоящее время я пытаюсь вернуть содержимое файла.

// This line writes my content byte[] array to disk.  This file when opened with gzip works fine.
System.IO.File.WriteAllBytes(@"C:\temp\test.vcp", result.FileBytes);


// Writing out the byte array to the Response results in a corrupt file.  I have also attempted to Response.WriteFile(@"C:\temp\test.vcp") which also results in a corrupt file.

Response.Clear();
Response.ContentType = "application/gzip";

Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", result.FileBytes.Length.ToString());

Response.BinaryWrite(result.FileBytes);
Response.Flush();
Response.Close();
Response.End();

Поскольку созданный мной файл может быть записан на диск и может быть прочитан с помощью Gzip, но файл, полученный браузером, поврежден, я уверен,что мое создание файла в порядке.Но каким-то образом после записи файла в Response он поврежден.

Я действительно задавался вопросом, может быть, какой-то HTTPHandler манипулирует результатом, но я не добавил никаких обработчиков (которые я вижу).

Я в настоящее время запускаю приложение локально через IISExpress.Как я могу проверить, какие HttpHandlers / HttpModules применяются к конвейеру?

В конечном итоге я ожидаю получить в браузере тот же файл, что записан на диск.

Для справки, мой сгенерированныйсодержимое имеет длину 132 байта, но браузер получает 216 байтов.Я заметил, что, глядя на структуру байтов полученных данных, в контенте есть повторяющийся шаблон из 3 байтов со значениями 239, 191, 189. Это выглядит почти так, как будто результирующий байтовый массив был заполнен или дополнен этими данными.3 байта.

РЕДАКТИРОВАТЬ

Вот автономный метод действий, который продемонстрировал проблему.

    [HttpGet]
    public void GetFile()
    {

        byte[] text = Encoding.ASCII.GetBytes(@"{""PetName"":""Doggy McDocFace"",""OwnerName"":""Kurt""}");
        byte[] compressed = Compress(text);

        var cd = new System.Net.Mime.ContentDisposition
        {
            // for example foo.bak
            FileName = "ExampleFile.vcp",

            // always prompt the user for downloading, set to true if you want 
            // the browser to try to show the file inline
            Inline = true,
        };


        System.IO.File.WriteAllBytes(@"C:\temp\ExampleFile.vcp", compressed);


        Response.Clear();
        Response.ContentType = "application/gzip";

        Response.AppendHeader("Content-Disposition", cd.ToString());
        Response.AddHeader("Content-Length", compressed.Length.ToString());

        Response.BinaryWrite(compressed);
        Response.Flush();
        Response.Close();
        Response.End();
    }

    public byte[] Compress(byte[] raw)
    {
        using (var memory = new MemoryStream())
        {
            using (var gzip = new GZipStream(memory, CompressionMode.Compress, true))
            {
                gzip.Write(raw, 0, raw.Length);
            }
            return memory.ToArray();
        }
    }

Здесь я подделываю мой JSON-контент,а затем сжимая его.Файл, записанный на диск, работает нормально, и его можно открыть с помощью моего приложения GZip (я использую 7-zip).Однако файл, полученный браузером, поврежден.7-zip не может распознать его как файл gzip.

РЕДАКТИРОВАТЬ 2

Так что (благодаря @Will) выглядит, что содержимое при записи в Response падаетфол кодировки UTF-8.Я не могу понять, как, хотя, как в моем примере выше, я использую Encoding.ASCII.GetBytes (), чтобы преобразовать мою строку в массив byte [].

Я попытался установить

       Response.Charset = Encoding.ASCII.EncodingName;
       Response.ContentEncoding = Encoding.ASCII;

Но это все равно не приводит к загрузке действительного файла.

Редактировать 3

Я сузил проблему до шифрования GZipданные.Если я не зашифрую данные, то текстовый файл загружается нормально.Однако шифрование массива byte [] и последующая запись этого массива byte [] в Repsonse приводят к проблемам с кодировкой UTF-8.Любые байты со значением более 127 повреждены 3 байтами, о которых я упомяну далее.Я не могу понять, почему Ответ обрабатывает эти зашифрованные данные таким образом.Я предполагаю, что когда массив Byte [] представляет собой простой текст в виде массива byte [], тогда это обрабатывается нормально.Как только это будет правильный массив byte [], т. Е. Не просто строка в виде массива byte [], тогда в Response будет происходить другое преобразование кодировки.

1 Ответ

0 голосов
/ 16 мая 2019

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

GZip / Сжатие сжатия в ASP.NET MVC

public class CompressAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
        if (string.IsNullOrEmpty(encodingsAccepted)) return;

        encodingsAccepted = encodingsAccepted.ToLowerInvariant();
        var response = filterContext.HttpContext.Response;

        if (encodingsAccepted.Contains("deflate"))
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
        else if (encodingsAccepted.Contains("gzip"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
    }
}
[Compress]
[HttpGet]
public ActionResult GetFile()
{...}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...