本地服务器存储

引用RsCode.Storage.LocalStorage

使用服务

services.AddLocalStorage(Configuration);

七牛服务器存储

以客户端上传方式为例,步骤:
1.客户端传递key,向上传API索取上传token信息
2.客户端收到token信息后,处理上传url与key信息
3.客户端执行上传 ,附加额外的参数{key:"key",token:"上传token"}

引用RsCode.Storage.QiniuStorage

使用服务

services.AddQiniuStorage(Configuration);

//七牛存储配置
  "QiniuStorage": {
    "UploadTokenUrl":"自定义的token获取地址", 
	"ManageTokenUrl":"自定义的token获取地址", 
	"DownloadTokenUrl":"自定义的token获取地址", 
    "AccessKey": "x",
    "SecretKey": "x",
    "Domain": "在七牛云存储配置的域名", //上传后资源地址,例:http://res.rscode.cn
    "Bucket": "在七牛云存储配置的bucket",
    "Zone": "bucket存储区域", //华东 ZONE_CN_East  华北 ZONE_CN_North 华南 ZONE_CN_South  北美 ZONE_US_North
    "UploadTokenExpireTime": 30, // 上传token有效时间单位:秒
    "DownloadTokenExpireTime": 5,//下载token有效时间
    "ManageTokenExpireTime": 5   //管理token有效时间
  }
  
  //本地存储配置 
  "LocalStorage": {
    "UploadTokenUrl":"自定义的token获取地址", //例 /storage/uptoken
	"UploadUrl": "https://localhost:5001/storage/upload",
    "AccessKey": "",//不填
    "SecretKey": "",//不填
    "Domain": "上传后资源地址", //上传后资源地址,例:http://res.rscode.cn
    "Bucket": "", //不填
    "Zone": "", //不填
    "UploadTokenExpireTime": 30, // 上传token有效时间单位:秒
    "DownloadTokenExpireTime": 5,//下载token有效时间
    "ManageTokenExpireTime": 5   //管理token有效时间
  }

自定义获取uptoken的逻辑 例:

public class StorageController : Controller
    {
        public IActionResult UpToken([FromServices]IEnumerable<IStorageProvider> providers,string provider="qiniu")
        {
            var qiniu = providers.FirstOrDefault(s => s.StorageName == provider);
            var tokenInfo= qiniu.GetUploadToken();
            return Json(tokenInfo);
        }
	[HttpPost]
        public async Task<IActionResult> Upload([FromServices] IEnumerable<IStorageProvider> providers)
        {
            var local = providers.FirstOrDefault(s => s.StorageName == "local");
            var tokenInfo= await local.UploadAsync();
            return Json(tokenInfo);
        }
    }

获取上传token的返回值: {domain:'合法域名',token:'上传token',upload_url:'上传地址'}

上传成功后的返回值 {res:'',key:'上传后的key'}

注意:使用本地上传时,在wwwroot文件夹中,确保保存文件的文件夹是存在的 razor页面,使用 @Html.Raw(info.UploadUrl)防止字符被转义

实例:

vue文件上传

html

@{
    Layout = null;
    ViewData["title"] = "上传demo";
    var info = ViewBag.TokenInfo as RsCode.Storage.TokenResult;
   
}
<el-upload class="avatar-uploader"
                   ref="upload"
                   :action="upload_url"
                   :data="upload_data"
                   :show-file-list="false"
                   :on-success="handleAvatarSuccess"
                   :on-error="handleError"
                   :before-upload="beforeAvatarUpload">
            <img v-if="imageUrl" :src="imageUrl" class="avatar">
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
        </el-upload>

js

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                imageUrl: '',
                // 七牛云上传储存区域的上传域名(华东、华北、华南、北美、东南亚)
                upload_url: "@Html.Raw(info.UploadUrl)",
                domain:"@info.Domain",
                upload_data: {
                    key: "",
                    token:"@info.Token"
                }
            };
        },
        methods: {
            handleAvatarSuccess(res, file) {
                console.log('res=' + JSON.stringify(res));
                console.log('file=' + JSON.stringify(file));
                if (res.key && res.key.length > 0) {
                    let fileUrl = this.domain + res.key;
                    //本地url
                    //this.imageUrl = URL.createObjectURL(file.raw);
                    this.imageUrl = fileUrl;
                } else {
                    this.handleError(res);
                }
            },
            handleError: function (res) {
                this.$message({
                    message: "上传失败,请重试",
                    duration: 2000,
                    type: "warning"
                });

                if (res.error == "expired token")
                {
                    //重新获取token
                }
            },
            beforeAvatarUpload(file) {
                const isJPG = file.type === 'image/jpeg';
                const isLt2M = file.size / 1024 / 1024 < 2;

                if (!isJPG) {
                    this.$message.error('上传头像图片只能是 JPG 格式!');
                }
                if (!isLt2M) {
                    this.$message.error('上传头像图片大小不能超过 2MB!');
                }

                let key = file.name; // 文件资源名
                //wwwroot下准备好文件夹file
                key = 'file/' + Math.random().toString(36).substr(2) + '.' + key.split(".")[1];
                this.upload_data.key = key;
                return isJPG && isLt2M;
            },
            getUploadToken() {
                this.http.get("/storage?provider=qiniu&action=uptoken")
                    .then(ret => {
                        this.domain = ret.domain;
                        this.token = ret.token;
                    })
                    .catch(err => {

                    })
            } 
        }
    })
</script>

css

<style>
        .avatar-uploader .el-upload {
            border: 1px dashed #d9d9d9;
            border-radius: 6px;
            cursor: pointer;
            position: relative;
            overflow: hidden;
        }

            .avatar-uploader .el-upload:hover {
                border-color: #409EFF;
            }

        .avatar-uploader-icon {
            font-size: 28px;
            color: #8c939d;
            width: 178px;
            height: 178px;
            line-height: 178px;
            text-align: center;
        }

        .avatar {
            width: 178px;
            height: 178px;
            display: block;
        }
</style>
Ant Blazor 上传
<Upload Action="@tokenResult.UploadUrl"
           Data="@tokenResult.Data"
       Name="file"
       @bind-FileList="fileList"
       ShowButton="fileList?.Count <= FileCount"
       ListType="picture-card"
       OnPreview="(file)=> {
                   Console.WriteLine(file.FileName);
                   Console.WriteLine(file.Url);
                   previewVisible = true;
                   previewTitle = file.FileName;
                   imgUrl = file.Url;
               }"
       BeforeAllUploadAsync="BeforeUploadAsync"
       OnChange="HandleChange"
       OnSingleCompleted="OnSingleCompleted"
       OnCompleted="Completed">
   <div>
       <Icon Type="plus"></Icon>
       <div className="ant-upload-text">上传图片</div>
   </div>

</Upload>

上传前,BeforeUpload 或BeforeAllUploadAsync 如果返回false时会阻止上传 上传中,HandleChange 处理上传过程 上传成功 OnSingleCompleted

事件处理代码

UploadTokenResult tokenResult { get; set; } = new UploadTokenResult();
       List<UploadFileItem> fileList = new List<UploadFileItem>
      {
           //new UploadFileItem
           //{
           //    Id = "1",
           //    FileName = "image.png",
           //    State = UploadState.Success,
           //    Url = "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
           //},
        
      };

       bool loading = false;
       bool BeforeUpload(UploadFileItem file)
       {
           var isJpgOrPng = file.Type == "image/jpg" || file.Type == "image/jpeg" || file.Type == "image/png";
           if (!isJpgOrPng)
           {
               _message.Error("只能上传格式为JPG或PNG的图片文件!");
           }
           var isLt2M = file.Size / 1024 / 1024 < 2;
           if (!isLt2M)
           {
               _message.Error("图片必须小于2MB!");
           }

           return isJpgOrPng && isLt2M;
       }
       async Task<bool>BeforeUploadAsync(List<UploadFileItem> items)
       {
           try
           {
               foreach (var file in items)
               {
                   if(BeforeUpload(file))
                   {
                      await GetUploadTokenAsync(file);
                   }
               }
               return true;
           }
           catch (AppException ex)
           {
              await _message.Error(ex.Message);
           }
           catch (Exception e)
           {
              await _message.Error(e.Message);
           }
           return false;
       }
       
       void HandleChange(UploadInfo file)
       {
           Console.WriteLine("start handleChange");
           Console.WriteLine("tokenResult="+JsonSerializer.Serialize(tokenResult));
           if (string.IsNullOrEmpty(tokenResult.UploadUrl))
           {
               _message.Error("未找到上传服务器"); 
           }else
           {
               loading = file.File.State == UploadState.Uploading;

               if (file.File.State == UploadState.Success)
               {
                   //imgUrl = file.File.ObjectURL;
               }
               InvokeAsync(StateHasChanged).GetAwaiter().GetResult();
               Console.WriteLine("HandleChange=" + JsonSerializer.Serialize(file));
           }
       }
       void OnSingleCompleted(UploadInfo fileinfo)
       { 
           if (fileinfo.File.State == UploadState.Success)
           {
               var result = fileinfo.File.GetResponse<ResponseModel>();
               var res=fileinfo.File.GetResponse<QiniuResponse>();
               fileinfo.File.Url = $"{tokenResult.Domain}{res.key}";
           }
       }
       void Completed(UploadInfo info)
       {
           
       }
       void OnPreview(UploadFileItem file)
       { 
           previewVisible = true;
           previewTitle = file.FileName;
           imgUrl = file.Url;
           OnPreviewImage.InvokeAsync(imgUrl).GetAwaiter().GetResult();
       }
       
       
       async Task<bool> OnRemove(UploadFileItem file)
       {
           await OnRemoveImage.InvokeAsync(file.Url);
           return false;
       }
       async Task GetUploadTokenAsync(UploadFileItem file)
       {           
           string key = $"{Guid.NewGuid().ToString("N")}{file.Ext}"; 
          tokenResult = await UploadService.GetStorageTokenAsync(UserToken, key); 
       }

上传成功返回json

{
  "File": {
    "Id": "63e9ded5-c68b-4367-b1a6-717abcb431dc",
    "FileName": "e78b1da352e7653031d9bbdc2b07b90b.jpg",
    "Percent": 100,
    "ObjectURL": "blob:https://localhost:5001/20a473a3-67cc-45c5-9f24-3e4f6d83c031",
    "Url": null,
    "Response": "{\"hash\":\"FlN6RcwiE9aR-GKN9tSCVeFb4LUG\",\"key\":\"mt_9215508b6c164307ba0a7985450da24b_e878fba96d0a4b38989713b21f1630f3\"}",
    "State": 0,
    "Size": 460427,
    "Ext": ".jpg",
    "Type": "image/jpeg"
  },
  "FileList": [
    {
      "Id": "63e9ded5-c68b-4367-b1a6-717abcb431dc",
      "FileName": "e78b1da352e7653031d9bbdc2b07b90b.jpg",
      "Percent": 100,
      "ObjectURL": "blob:https://localhost:5001/20a473a3-67cc-45c5-9f24-3e4f6d83c031",
      "Url": null,
      "Response": "{\"hash\":\"FlN6RcwiE9aR-GKN9tSCVeFb4LUG\",\"key\":\"mt_9215508b6c164307ba0a7985450da24b_e878fba96d0a4b38989713b21f1630f3\"}",
      "State": 0,
      "Size": 460427,
      "Ext": ".jpg",
      "Type": "image/jpeg"
    }
  ]
}