【问题标题】:stream.CopyAsync(streamDestination) its throw Stream does not support reading exceptionstream.CopyAsync(streamDestination) 其抛出 Stream 不支持读取异常
【发布时间】:2020-11-25 21:48:07
【问题描述】:

我正在尝试为 gzip 压缩文件,但是在压缩后我需要获取生成的 gzip 字节以生成 base64 字符串。

但是,当我尝试将 gzip 流复制到内存流时,它会引发异常 Stream 不支持读取。

我找不到解决此问题的方法,有人可以帮助我吗?

按照下面的代码:

    public static async Task<Tuple<bool, string, string>> CompressToGzipBase64(this IFormFile formFile, Language language)
    {
        var filePath = formFile.FileName + ".gz";

        try
        {
            await using var gzipFileStream = File.OpenWrite(formFile.FileName + ".gz");
            await using var gZipStream = new GZipStream(gzipFileStream, CompressionMode.Compress);
            await formFile.CopyToAsync(gZipStream);

            var bytesOfFile = await ConverteStreamToByteArray(gZipStream);

            var fileInBase64 = Convert.ToBase64String(bytesOfFile);

            File.Delete(filePath);

            return new Tuple<bool, string, string>(true, fileInBase64, string.Empty);
        }
        catch (Exception e)
        {
            File.Delete(filePath);

            return language switch
            {
                Language.PtBr => new Tuple<bool, string, string>(false, string.Empty,
                    "Ocorreu um erro durante a conversão do arquivo para o formato Gzip. Por favor tente a operação novamente!"),
                Language.EnUs => new Tuple<bool, string, string>(false, string.Empty,
                    "An error occurred while converting the file to Gzip format. Please try the operation again!"),
                Language.EsEs => new Tuple<bool, string, string>(false, string.Empty,
                    "Se produjo un error al convertir el archivo a formato Gzip. ¡Intente la operación nuevamente!"),
                _ => new Tuple<bool, string, string>(false, string.Empty, string.Empty)
            };
        }
    }

    private static async Task<byte[]> ConverteStreamToByteArray(GZipStream stream)
    {
        await using var ms = new MemoryStream();
        await stream.CopyToAsync(ms);
        return ms.ToArray();
    }

【问题讨论】:

    标签: c# .net-core gzip


    【解决方案1】:

    您无法在压缩模式下从 GzipStream 中读取数据,因为它是一个转换流,而不是数据所在的位置。您的数据实际上在 FileStream 中,因此您需要从中读取。

    但是你甚至不需要 FileStream,因为你没有对文件做任何事情,因为你在最后删除它。 FileStream 是不必要的,因为它需要写入磁盘非常不高效。

    那就这样吧:

    await using var compressedStream = new MemoryStream();
    await using var gZipStream = new GZipStream(compressedStream, CompressionMode.Compress);
    await formFile.CopyToAsync(gZipStream);
    
    var bytesOfFile = compressedStream.ToArray();
    
    var fileInBase64 = Convert.ToBase64String(bytesOfFile);
    

    另外,我建议使用值元组而不是元组,因为它们更简洁、更高效且更易读,因为您可以命名元素。

    所以你的整个代码可以改进为:

    public static async Task<(bool success, string base64, string error)> CompressToGzipBase64(this IFormFile formFile, Language language)
    {
        try
        {
          await using var compressedStream = new MemoryStream();
          await using var gZipStream = new GZipStream(compressedStream, CompressionMode.Compress);
          await formFile.CopyToAsync(gZipStream);
    
          var bytesOfFile = compressedStream.ToArray();
    
          var fileInBase64 = Convert.ToBase64String(bytesOfFile);
    
          return (true, fileInBase64, string.Empty);
        }
        catch (Exception e)
        {
            return language switch
            {
                Language.PtBr => (false, string.Empty,
                    "Ocorreu um erro durante a conversão do arquivo para o formato Gzip. Por favor tente a operação novamente!"),
                Language.EnUs => (false, string.Empty,
                    "An error occurred while converting the file to Gzip format. Please try the operation again!"),
                Language.EsEs => (false, string.Empty,
                    "Se produjo un error al convertir el archivo a formato Gzip. ¡Intente la operación nuevamente!"),
                _ => (false, string.Empty, string.Empty)
            };
        }
    }
    

    您可以通过以下方式调用它:

    var (success, base64, error) = await formFile.CompressToGzipBase64(Language.EnUs); 
    

    另一个建议:不要只丢弃异常消息。您应该在某处记录异常或在错误消息中包含异常文本。当抛出异常时,这使得调试和生活变得更加容易,因为您无需猜测真正发生了什么错误/异常。

    【讨论】:

    • 我使用的api也是通过文件名验证的,文件名必须包含.gz扩展名,是否可以在压缩过程中也更改流文件的名称?
    • 在您的代码中创建一个 gz 文件,写入该文件,然后将其删除。因此,在调用 CompressToGzipBase64 后,该文件不再存在。而且您的代码甚至不会返回您声明的正在验证的 gz 文件名。那么验证在哪里发生,它实际上做了什么?请详细说明您的问题。
    • 嗨 cKuri,所以我使用了另一个为我验证文件的 api,但它只接受 base64 字符串,这个 base64 字符串必须是转换后的文件(txt、xml、excel 等)为 gzip 格式。当这个其他 api 接收到 base64 字符串时,它会检查我发送的文件的名称是否以 .gz 结尾,然后解压缩 .gz 文件并读取其中的文件。所以我需要为我创建一个 .gz 文件的过程,将 IForm 中的文件放入 .gz 文件中,然后将此 .gz 文件转换为 base64 字符串。
    • 是否可以在不必在机器上物理创建 .gz 文件的情况下执行此过程?我希望这个过程现在已经变得清晰了。
    猜你喜欢
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-22
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多