zoukankan      html  css  js  c++  java
  • (多张图片打包为Zip返回前端下载) 记NetCore HttpClient.GetStreamAsync()返回只读流,Stream的Length属性不可用,报错的问题。

    需求是做一个打包若干照片为.zip格式,返回给Vue前端以供下载。

    照片地址存在于数据库中,文件存储在Minio中,地址是https的,我用httpClient下载,有个问题,SSL证书不正确,此处参考网上给出的答案已经解决。

    报错如下:

    System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
    ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid because of errors in the certificate chain: PartialChain
    at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
    at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
    at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Boolean async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
    --- End of inner exception stack trace ---

    解决方案如下:

                //绕过https证书             
                var httpClientHandler = new HttpClientHandler();
                httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true; 
                HttpClient client = new HttpClient(httpClientHandler);
    

    参考https://www.cnblogs.com/GarsonZhang/p/13039342.html老哥的《.NetCore下多个文件流生成压缩文件》文章,把他的通过aws api下载流的内容替换为httpClient下载。

    代码如下:

            /// <summary>
            /// 创建Zip压缩文件,网络图片
            /// </summary>
            /// <param name="urls"></param>
            /// <returns></returns>
            public static async Task<byte[]> CreateZipBytesAsync(List<string> urls)
            {
                byte[] bytes;
                using (MemoryStream ms = new MemoryStream())
                {
                    using (ZipArchive zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
                    {
                        int index = 1;
                        foreach (var url in urls)
                        {
                            //得到文件流
                            var picBytes = await DownloadImageAsync(url);
                            ZipArchiveEntry entry = zip.CreateEntry($"{index}.jpg");
    
                            #region 测试获取的文件流有没有问题
                            //FileStream fs = new FileStream("D:\test\" + index + ".jpg", FileMode.Create);
                            //fs.Write(picBytes, 0, picBytes.Length);
                            //fs.Dispose(); 
                            #endregion
    
                            using (Stream sw = entry.Open())
                            {
                                sw.Write(picBytes, 0, picBytes.Length);//将文件的字节写到$"{index}.jpg"中
                            }
                            index++;
                        }
                        InvokeWriteFile(zip);//重新计算压缩文件的大小
                        int nowPos = (int)ms.Position;
                        bytes = new byte[ms.Length];
                        ms.Position = 0;
                        ms.Read(bytes, 0, bytes.Length);
                        ms.Position = nowPos;
                    }
                    return bytes;
                }
            }
            private static void InvokeWriteFile(ZipArchive zipArchive)
            {
                foreach (MethodInfo method in zipArchive.GetType().GetRuntimeMethods())
                {
                    if (method.Name == "WriteFile")
                    {
                        method.Invoke(zipArchive, new object[0]);
                    }
                }
            }
    
            private static async Task<Stream> DownloadImageAsync(string url)
            {
                // 绕过https证书
                var httpClientHandler = new HttpClientHandler();
                httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true;
               
                //注意这里生成的只读流无法使用Length属性
                HttpClient client = new HttpClient(httpClientHandler);
                client.BaseAddress = new Uri(url);
                var stream = await client.GetStreamAsync(url);
                return stream;
            }
            /// 将 Stream 转成 byte[]
            public byte[] StreamToBytes(Stream stream)
            {
                byte[] bytes = new byte[stream.Length];//这里会报错'((System.Net.Http.HttpBaseStream)stream).Length' threw an exception of type。。。
                stream.Read(bytes, 0, bytes.Length);
                // 设置当前流的位置为流的开始
                stream.Seek(0, SeekOrigin.Begin);
                return bytes;
            }
    

    然后便是各种百度,也没有找到正确的写法,最后bing,参考了https://stackoverflow.com/questions/41027999/how-to-use-httpclient-getstreamasync-method文章,修改DownloadImageAsync方法如下:

            /// <summary>
            /// 下载单张图片流
            /// </summary>
            /// <param name="url"></param>
            /// <returns></returns>
            private static async Task<byte[]> DownloadImageAsync(string url)
            {
                #region 绕过https证书
                var httpClientHandler = new HttpClientHandler();
                httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true;
                #endregion
    
                HttpClient client = new HttpClient(httpClientHandler);
                using (var file = await client.GetStreamAsync(url).ConfigureAwait(false))
                using (var memoryStream = new MemoryStream())
                {
                    await file.CopyToAsync(memoryStream);
                    return memoryStream.ToArray();
                }
            }
    

    完整代码如下:

       public class ZipHelper
       {
            /// <summary>
            /// 创建Zip压缩文件,网络图片
            /// </summary>
            /// <param name="urls"></param>
            /// <returns></returns>
            public static async Task<byte[]> CreateZipBytesAsync(List<string> urls)
            {
                byte[] bytes;
                using (MemoryStream ms = new MemoryStream())
                {
                    using (ZipArchive zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
                    {
                        int index = 1;
                        foreach (var url in urls)
                        {
                            //得到文件流
                            var picBytes = await DownloadImageAsync(url);
                            ZipArchiveEntry entry = zip.CreateEntry($"{index}.jpg");//压缩文件内创建一个文件名为$"{index}.jpg",流是什么文件格式就用什么格式
    
                            #region 测试获取的文件流有没有问题
                            //FileStream fs = new FileStream("D:\test\" + index + ".jpg", FileMode.Create);
                            //fs.Write(picBytes, 0, picBytes.Length);
                            //fs.Dispose(); 
                            #endregion
    
                            using (Stream sw = entry.Open())
                            {
                                sw.Write(picBytes, 0, picBytes.Length);//将文件的字节写到$"{index}.jpg"中
                            }
                            index++;
                        }
                        InvokeWriteFile(zip);//重新计算压缩文件的大小
                        int nowPos = (int)ms.Position;
                        bytes = new byte[ms.Length];
                        ms.Position = 0;
                        ms.Read(bytes, 0, bytes.Length);
                        ms.Position = nowPos;
                    }
                    return bytes;
                }
            }
    
            private static void InvokeWriteFile(ZipArchive zipArchive)
            {
                foreach (MethodInfo method in zipArchive.GetType().GetRuntimeMethods())
                {
                    if (method.Name == "WriteFile")
                    {
                        method.Invoke(zipArchive, new object[0]);
                    }
                }
            }
    
            /// <summary>
            /// 下载单张图片流
            /// </summary>
            /// <param name="url"></param>
            /// <returns></returns>
            private static async Task<byte[]> DownloadImageAsync(string url)
            {
                #region 绕过https证书
                var httpClientHandler = new HttpClientHandler();
                httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true;
                #endregion
    
                HttpClient client = new HttpClient(httpClientHandler);
                using (var file = await client.GetStreamAsync(url).ConfigureAwait(false))
                using (var memoryStream = new MemoryStream())
                {
                    await file.CopyToAsync(memoryStream);
                    return memoryStream.ToArray();
                }
            }
        }
    

    参考内容:
    https://stackoverflow.com/questions/41027999/how-to-use-httpclient-getstreamasync-method (感谢!)
    https://www.cnblogs.com/GarsonZhang/p/13039342.html (感谢!)

  • 相关阅读:
    测试开发进阶——spring boot——MVC——thymeleaf模板——通过Model model的model.addAttribute返回数据给模板——示例01
    测试开发进阶——spring boot——MVC——thymeleaf模板——打开网页
    git小笔记
    web.config中sessionState节点的配置方案
    【BZOJ1053】 反素数ant
    正则表达式 (python 2)
    简政放权是合理的,大道至简
    一本通1035
    一本通1033
    Oracle中事务隔离机制
  • 原文地址:https://www.cnblogs.com/gyl5201314/p/14889009.html
Copyright © 2011-2022 走看看