zoukankan      html  css  js  c++  java
  • 从客户端浏览器直传文件到Storage

    关于上传文件到Azure Storage没有什么可讲的,不论我们使用哪种平台、语言,上传流程都如下图所示:

    从上图我们可以了解到从客户端上传文件到Storage,是需要先将文件上传到应用服务上,然后再通过应用服务器上的后端代码将文件上传到Storage,在这里应用服务器就是起到一个代理的作用。

    当然,这是最常规的做法,但是确有一个致命的弱点,那就是我们上传文件到Storage中,是要占用应用服务器的计算、网络、IO等资源的,这对于一些对性能要求很高的应用场景是很难接受的。

    那么,我们是否有解决方案?答案是肯定的,我们可以跳过应用服务器从客户端将文件直传到Storage中,那么我们如何来构建这套解决方案呢?这就是我们下面要讲的。

    我们先来看下直传文件到Storage的流程图:

    这里特别有几点需要注意的地方:

    1、  不能使用存储账号和存储密钥来验证身份进行文件上传,因为这会在客户端暴露存储账号和存储密钥等敏感信息。

    2、  Storage默认是不能跨域访问的,所以我们需要将应用服务器所在域加入到Storage的跨域规则中。

    3、  通过上图,我们可以看到客户端一共发送两次http请求,第一次从应用服务器获取Shared Access Signature(具有写权限),再带着获取到的SAS将文件从客户端上传到Storage。

    大致流程和思路已经讲清楚了,那么下面我们就来看看用代码是如何实现的。

    Index.html

    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title></title>
        <meta charset="utf-8" />
        <title>直传文件到Storage</title>
        <script src="~/Scripts/jquery-1.10.2.js"></script>
        <script src="~/Scripts/uploadfile.js"></script>
    </head>
    <body>
        <input type="file" id="file" name="file" />
    
        <div id="output">
            <strong>File properties:</strong>
            <br />
            <p>
                Name: <span id="fileName"></span>
            </p>
            <p>
                File size: <span id="fileSize"></span>bytes.
            </p>
            <p>
                File type: <span id="fileType"></span>
            </p>
            <p>
                <input type="button" value="Upload File" id="uploadFile" />
            </p>
        </div>
    </body>
    </html>

    updatefile.js

    (function ($) {
        var reader = null,
            requestUri = null,
            selectedFile = null,
            blobName = null;
    
        function sendAjax(url, dataToSend, beforeSendFunction, successFunction) {
            $.ajax({
                url: url,
                type: "PUT",
                data: dataToSend,
                processData: false,
                beforeSend: beforeSendFunction,
                tryCount: 0,
                success: successFunction,
    
            });
        }
    
        function readerOnLoadEnd(evt) {
            if (evt.target.readyState === FileReader.DONE) {
                var uri = requestUri,
                    requestData = new Uint8Array(evt.target.result);
                sendAjax(uri, requestData,
                    function (xhr) {
                        xhr.setRequestHeader("x-ms-blob-type", "BlockBlob");
                        xhr.setRequestHeader("x-ms-blob-content-type", getContentType(blobName));
                    }, function (data, status) {
                        alert("upload success.");
                    });
            }
        };
    
        function handleFileSelect(e) {
            selectedFile = e.target.files[0];
            blobName = selectedFile.name;
            var fileSize = selectedFile.size;
            $('#output').show();
            $('#fileName').text(blobName);
            $('#fileSize').text(fileSize);
            $('#fileType').text(selectedFile.type);
    
            $.get('{获取Blob SAS的接口地址}', { 'blobName': blobName }, function (data) {
                console.log(data);
                requestUri = data;
            });
        }
    
        function startUpload() {
            $("#uploadFile").prop('disabled', true);
            $("#file").prop('disabled', true);
            var slice = selectedFile.slice(0, selectedFile.size);
            reader.readAsArrayBuffer(slice);
        }
    
        function getContentType(blobName) {
            var ext = blobName.substr(blobName.lastIndexOf('.'));
            var contentType = 'application/octet-stream';
            switch (ext) {
                case ".txt":
                    contentType = "text/plain";
                    break;
                case ".png":
                    contentType = "image/png";
                    break;
                case ".zip":
                    contentType = "application/zip";
                    break;
                case ".pptx":
                    contentType = "application/vnd.ms-powerpoint";
                    break;
                case ".docx":
                    contentType = "application/msword";
                    break;
                case ".pdf":
                    contentType = "application/pdf";
                    break;
                case ".jpg":
                case ".jpeg":
                    contentType = "image/jpeg";
                    break;
                case ".html":
                    contentType = "text/html";
                    break;
                case ".js":
                    contentType = "application/x-javascript";
                    break;
                case ".css":
                    contentType = "text/css";
                    break;
                case ".gif":
                    contentType = "image/gif";
                    break;
                case ".ico":
                    contentType = "image/x-icon";
                    break;
                case ".mp4":
                    contentType = "video/mpeg4";
                    break;
                case ".mp3":
                    contentType = "audio/mp3";
                    break;
                default:
                    break;
            }
            return contentType;
        }
    
        $(function () {
            $('#output').hide();
            if (window.File && window.FileReader && window.FileList && window.Blob) {
                reader = new FileReader();
                reader.onloadend = readerOnLoadEnd;
            } else {
                alert('The File APIs are not fully supported in this browser.');
                $('#file').prop('disabled', true);
                return;
            }
            $('#file').bind('change', handleFileSelect);
            //上传文件
            $('#uploadFile').bind('click', startUpload);
        });
    }(jQuery))

    后端提供的Shared Access Signature接口代码

    private string _StorageConnectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
    private string _AccountName = ConfigurationManager.AppSettings["AccountName"];
    private string _AccountKey = ConfigurationManager.AppSettings["AccountKey"];
    private string _ContainerName = ConfigurationManager.AppSettings["ContainerName"];
    
    [HttpGet]
    public string GetRequestStorageUri(string blobName)
    {
        if (string.IsNullOrEmpty(blobName))
        {
              throw new ArgumentNullException();
         }
        if (string.IsNullOrEmpty(_ContainerName)) {
              throw new NullReferenceException("container is null or empty");
         }
    
    CloudBlockBlob blockBlob = GetCloudBlockBlob(_ContainerName, blobName);
        string sas = GetBlobSharedAccessSignature(blockBlob);
        string requestUri = string.Format("https://{0}.blob.core.chinacloudapi.cn/{1}/{2}{3}", _AccountName, _ContainerName, blobName, sas);
        return requestUri;
    }
    
    private CloudBlockBlob GetCloudBlockBlob(string containerName,string blobName)
    {
        if (string.IsNullOrEmpty(containerName) || string.IsNullOrEmpty(blobName))
        {
            throw new ArgumentNullException();
         }
         if (string.IsNullOrEmpty(_StorageConnectionString))
         {
             throw new NullReferenceException("storage connection string is null or empty");
           }
            CloudStorageAccount account = CloudStorageAccount.Parse(_StorageConnectionString);
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            ServiceProperties serviceProperties = blobClient.GetServiceProperties();
            serviceProperties.Cors.CorsRules.Clear();
            serviceProperties.Cors.CorsRules.Add(new CorsRule
            {
                AllowedOrigins = new List<string> { "{应用服务器所在域}" },
                AllowedMethods = CorsHttpMethods.Get| CorsHttpMethods.Put| CorsHttpMethods.Post| CorsHttpMethods.Delete,
                AllowedHeaders = new List<string> { "x-ms-*", "content-type", "accept" },
                MaxAgeInSeconds = 1 * 60 * 60
            });
            blobClient.SetServiceProperties(serviceProperties);
            CloudBlobContainer container = blobClient.GetContainerReference(containerName);
            container.CreateIfNotExists();
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);
            return blockBlob;
    }
    
    private string GetBlobSharedAccessSignature(CloudBlob blob) {
        if (blob == null)
        {
            throw new ArgumentNullException("blob is null");
         }
         return blob.GetSharedAccessSignature(new SharedAccessBlobPolicy {
             Permissions = SharedAccessBlobPermissions.Add| SharedAccessBlobPermissions.Create| SharedAccessBlobPermissions.Delete| SharedAccessBlobPermissions.List| SharedAccessBlobPermissions.Read| SharedAccessBlobPermissions.Write,
             SharedAccessStartTime = DateTime.UtcNow,
             SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5)
         });
    }
  • 相关阅读:
    OpenStack 发行版本
    刷新linux硬盘存储接口
    LVM实践
    LVM man帮助
    ansible --help 文档
    nmcli connection modify eth1 ipv4.addr "192.168.31.23" ipv4.method manual
    自己动手使用, MetaWeblog 发布博客(cnblogs)
    测试图片上传 on Markdown editor
    大批量更新数据mysql批量更新的四种方法
    PHP print_r 转换/还原为数组
  • 原文地址:https://www.cnblogs.com/rampb/p/6878456.html
Copyright © 2011-2022 走看看