Dynamics CRM中自定义页面实现附件管理包含下载模板、上传、下载、删除

前言
附件使用的Dynamics CRM平台本身的注释表annotation存储,将附件转换成二进制字节流保存到数据库中,因自带的注释在页面中显示附件不够直观,特做了一个单独的附件管理自定义页面,通过CRM自定义按钮打开对话框的方式展示附件列表页面。同时支持下载附件模板,页面为简单的H5+Bootstrap+CSS布局设计,通过ajax调用webAPI接口实现上传、下载、删除等操作,本文一并附上后台接口代码。
注意:本文中实现上传不支持多文件同时上传,需要多文件同时上传,可找现成的前端文件上传组件,本文中通过input类型'file'传递给后台接口的文件只支持接收一个文件,多文件上传需要修改下对应的后台上传接口,增加参数HttpPostedFileBase[] files,并在处理文件的逻辑改成遍历循环处理即可,其他内容一致。
文件上传方式
html页面中一个选择文件的input,type类型为'file',再一个上传文件的button按钮,然后做一个表格用来展示已上传的附件列表。选择完文件后,点击上传按钮,获取input中选择的file,js组装new FormData()对象,赋值后台接口所需要的参数,然后ajax调用webapi接口,注意一定要设置processData: false和contentType: false,不然后台接口接收不到文件。后台接口将接受的文件转成流保存到数据库中。
其他的文件下载和删除就不介绍了,比较简单。
效果图

代码
自定义按钮打开对话框页面

复制代码
 1 /**
 2  * 附件管理操作方法
 3  */
 4 function attachment() {
 5     console.log("附件")
 6     if (Xrm.Page.data.entity.getIsDirty()) {
 7         Xrm.Utility.alertDialog("请先保存后再上传附件!")
 8         return
 9     }
10     let EntityId = commonUtil.delBrackets(Xrm.Page.data.entity.getId())
11     let EntityName = Xrm.Page.data.entity.getEntityName()
12 
13     let params = { 'id': EntityId, 'type': EntityName}
14 
15     var DialogOption = new Xrm.DialogOptions
16     DialogOption.width = 900;
17     DialogOption.height = 650;
18     // 参数一:URL,参数二:窗体配置,参数三:Json参数,参数四:--,参数五:--
19     Xrm.Internal.openDialog("/WebResources/foton_accountchannelaccess_attachment", DialogOption, params, null, function (returnValue) {
20         console.log('调用成功 返回值:' + returnValue); //这里就可以接收到弹窗上面传过来的数组
21     });
22 }

附件管理HTML

复制代码
  1 <!DOCTYPE html>
  2 
  3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  4 <head>
  5     <meta charset="utf-8" />
  6     <title>附件管理</title>
  7     <script src="foton_Jquery.min.js"></script>
  8     <script src="ClientGlobalContext.js.aspx"></script>
  9     <script src="foton_kd_base_js"></script>
 10     <link href="foton_Newbootstrap.min.css" rel="stylesheet">
 11     <link href="foton_componentsrounded.min.css" rel="stylesheet">
 12     <link href="foton_components.min.css" rel="stylesheet">
 13     <style type="text/css">
 14         .fileinput-button input {
 15             position: static;
 16             opacity: 1;
 17             filter: none;
 18             font-size: inherit;
 19             direction: inherit;
 20         }
 21 
 22         .fileinput-button span {
 23             display: none;
 24         }
 25     </style>
 26 </head>
 27 <body style="overflow-wrap: break-word;">
 28     <div class="col-md-12" style="width: 100%; height: 100%;">
 29         <div class="portlet box portlet light portlet-fit bordered" style="height: 89%; margin-top: 40px;">
 30             <div class="portlet-body" style="height: 15%;">
 31                 <div class="row">
 32                     <div class="col-md-8">商业计划书新签</div>
 33                     <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl1">点击下载模板</label></div>
 34                 </div>
 35                 <div class="row">
 36                     <div class="col-md-8">商业计划书续签</div>
 37                     <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl2">点击下载模板</label></div>
 38                 </div>
 39                 <div class="row">
 40                     <div class="col-md-8">渠道基本信息评价表</div>
 41                     <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl3">点击下载模板</label></div>
 42                 </div>
 43                 <div class="row">
 44                     <div class="col-md-8">海外网络退出要素确认表</div>
 45                     <div class="col-md-4" style=" float: right"><label style="color: red;" for="lb_cl4">点击下载模板</label></div>
 46                 </div>
 47                 <div class="row">
 48                     <div class="col-md-8">公司简介</div>
 49                     <div class="col-md-4"><label style="color: red;"></label></div>
 50                 </div>
 51             </div>
 52             <div class="portlet-title" style="height: 5%;">
 53                 <span>
 54                     <label style="color: red;">请先下载模板,根据模板维护数据后再上传附件!</label>
 55                 </span>
 56                 <!--<form id='fileupload' action='/Mpa/Annotation/UpLoadAttmention' method='POST' enctype='multipart/form-data'>
 57                 </form>-->
 58                 <div class='row fileupload-buttonbar'>
 59                     <input type='hidden' id='FileName' name='FileName' value='' />
 60                     <input type='hidden' id='UploadType' name='UploadType' value='' />
 61                     <div class='col-lg-7 btnDelete' id='btnFileUploadId' style='margin-left: 30px; float: right'>
 62                         <span class='btn green fileinput-button'>
 63                             <i class='fa fa-plus'></i>
 64                             <span>选择文件</span>
 65                             <input type='file' name='fileUpload' id='fileUpload' onchange='document.getElementById("FileName").value = this.value.substr(this.value.lastIndexOf("\\") + 1)'>
 66                         </span>
 67                         <button type='button' class='btn blue start' onclick='SaveFiles()'>
 68                             <i class='fa fa-upload'></i>
 69                             <span> 上传文件 </span>
 70                         </button>
 71                     </div>
 72                 </div>
 73             </div>
 74 
 75             <div class="portlet-body table-scrollable" style="height: 80%; overflow: auto; max-height: 800px; padding: 0 10px">
 76                 <table class="table table-bordered table-hover">
 77                     <thead>
 78                         <tr id="opp">
 79                             <th width="150" style='vertical-align: middle;text-align: center;'> 文件名称 </th>
 80                             <th width="150" style='vertical-align: middle;text-align: center;'> 文件类型 </th>
 81                             <th width="150" style='vertical-align: middle;text-align: center;'> 创建时间 </th>
 82                             <th width="200" style='vertical-align: middle;text-align: center;'> 操作 </th>
 83                         </tr>
 84                     </thead>
 85                     <tbody id="Dataid"></tbody>
 86                 </table>
 87             </div>
 88         </div>
 89         </div>
 90 
 91 
 92     <script type="text/javascript">
 93         let entityId = window.getDialogArguments().id
 94         let entityName = window.getDialogArguments().type
 95         let queryStr = ""
 96         var apiUrl = ""
 97         var fileObj = document.getElementById("fileUpload")
 98         var outerHTML = fileObj.outerHTML;
 99         //页面加载
100         $(function () {
101             setWebAPIURL()
102             initOnload()
103             var label1 = $("label[for='lb_cl1']");
104             label1.on("click", function () {
105                 console.log("你点击了label1标签");
106                 downloadTemplate(1)
107             });
108             var label2 = $("label[for='lb_cl2']");
109             label2.on("click", function () {
110                 console.log("你点击了label2标签");
111                 downloadTemplate(2)
112             });
113             var label3 = $("label[for='lb_cl3']");
114             label3.on("click", function () {
115                 console.log("你点击了label3标签");
116                 downloadTemplate(3)
117             });  
118             var label4 = $("label[for='lb_cl4']");
119             label4.on("click", function () {
120                 console.log("你点击了label4标签");
121                 downloadTemplate(4)
122             });  
123         })
124         //设置webapi请求地址
125         function setWebAPIURL() {
126             if (Xrm.Page.context.getClientUrl().indexOf("Testf") >= 0)//测试环境
127                 apiUrl = "http://localhost:50887/"; //测试:http://10.100.56.13:8001/ ,本地:http://localhost:50887/
128             else if (Xrm.Page.context.getClientUrl().indexOf("hwdms") >= 0)//正式环境
129                 apiUrl = "https://hwapi.foton.com.cn/";
130         }
131         /**
132          * 页面初始化加载
133          */
134         function initOnload() {
135             //获取客户经销商窗体下的所有附件
136             queryStr = `/annotations?$select=annotationid,createdon,documentbody,filename,filesize,mimetype,_objectid_value,objecttypecode,subject&$filter=_objectid_value eq ${entityId}&$orderby=createdon desc`
137             commonUtil.queryWithUrl(queryStr, result => {
138                 $("#Dataid").empty();
139                 if (!result.success || !result.data || result.data.length < 1) {
140                     console.log("当前客户尚未上传附件!")
141                     return
142                 }
143                 console.log(result.data)
144                 for (var i = 0; i < result.data.length; i++) {
145                     var html = "<tr class='success'>"
146                         + "<td style='width: 5px;display:none;'> <input type='hidden' name='annotation_id"+ i+"' value='" + result.data[i]["annotationid"] + "'/>  </td>";
147                     //文件名称
148                     if (!!result.data[i]["filename"]) {
149                         html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["filename"] + "'> " + result.data[i]["filename"] + " </td>";
150                     } else {
151                         html += "<td width='150px;'></td>";
152                     }
153                     //文件类型
154                     if (!!result.data[i]["mimetype"]) {
155                         html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["mimetype"] + "'> " + result.data[i].filename.substr(result.data[i].filename.lastIndexOf(".") + 1) + " </td>";
156                     } else {
157                         html += "<td width='150px;'></td>";
158                     }
159                     //创建时间
160                     if (!!result.data[i]["createdon"]) {
161                         html += "<td width='150px;' align='center' valign='middle' title='" + result.data[i]["createdon"] + "'> " + result.data[i]["createdon@OData.Community.Display.V1.FormattedValue"] + " </td>";
162                     } else {
163                         html += "<td width='150px;'></td>";
164                     }
165                     //操作
166                     html += "<td width='200px;' align='center' valign='middle'>"
167                         + "<button class='btn blue start' type='button' onclick=DownloadFile('" + result.data[i].annotationid + "');>"
168                         + "<i class='fa fa-download'></i><span>下载</span>"
169                         + "</button> "
170                         + "<button class='btn red cancel obsbtnDelete' onclick= \"DeleteFile('" + result.data[i].annotationid + "')\"><i class='fa fa-trash'></i><span>删除</span></button>"
171                         + "</td> ";
172 
173                     html += "</tr>";
174 
175                     $("#Dataid").append(html + "<br/>");
176                 }
177 
178             }, false)
179 
180         }
181         /*
182         * 调用接口上传文件
183         */
184         function SaveFiles(callback) {
185             var files = document.getElementById("fileUpload").files[0]
186             if (files.length < 1) {
187                 Xrm.Utility.alertDialog("请先选择文件!")
188                 return
189             }
190             if (!callback) {
191                 overflowLayer.open("上传附件中....", SaveFiles)
192                 return
193             }
194             var formFile = new FormData();
195             formFile.append("entityId", entityId);//实体数据id
196             formFile.append("entityName", entityName);//实体名
197             formFile.append("files", files); //加入文件对象
198             $.ajax({
199                 url: apiUrl + "AccountChannelAccess/SaveAnnotation",
200                 type: "post",
201                 data: formFile,
202                 dataType: 'json',
203                 //mimeType: "multipart/form-data",
204                 async: true,  //使用同步的方式,true为异步方式
205                 processData: false,
206                 contentType: false,
207                 success: function (data, textStatus, xhr) {
208                     overflowLayer.close();
209                     if (data.code == 0) {
210                         Xrm.Utility.alertDialog(data.msg);
211                     } else {
212                         Xrm.Utility.alertDialog("上传成功");
213                         //刷新页面
214                         initOnload()
215                         //清除选择input里已上传的文件
216                         fileObj.outerHTML = outerHTML
217                     }
218                 },
219                 error: function (xhr, textStatus, errorThrown) {
220                     Xrm.Utility.alertDialog("请求接口【" + apiUrl + "】失败!请联系管理员!");
221                     overflowLayer.close();
222                 }
223             });
224         }
225         /**
226         * 删除已上传的文件
227         * @param {any} annotationId 附件id
228         */
229         function DeleteFile(annotationId) {
230             Xrm.Utility.confirmDialog("您确认要删除此附件吗?", function () {
231                 $.ajax({
232                     url: apiUrl + "AccountChannelAccess/DelAnnotation",
233                     type: "post",
234                     data: { "": annotationId },
235                     dataType: 'json',
236                     async: false,  //使用同步的方式,true为异步方式
237                     success: function (data, textStatus, xhr) {
238                         if (data.code == 0) {
239                             Xrm.Utility.alertDialog("删除失败!" + data.msg); 
240                         } 
241                         Xrm.Utility.alertDialog("删除成功!"); 
242                         initOnload()
243                     },
244                     error: function (xhr, textStatus, errorThrown) {
245                         Xrm.Utility.alertDialog("请求接口【" + apiUrl + "】失败!请联系管理员!")
246                     }
247                 });
248             });
249         }
250         /*
251         * 下载文件
252         * @param {any} annotationId 附件id
253         */
254         function DownloadFile(annotationId) {
255             console.log(annotationId);
256             location.href = apiUrl + 'AccountChannelAccess/DownloadAnnotation?id=' + annotationId;
257         }
258         /**
259          * 下载附件模板
260          * @param {any} type 1商业计划书新签,2商业计划书续签,3渠道基本信息评价表,4海外网络退出要素确认表
261          */
262         function downloadTemplate(type) {
263             //请求webAPI接口下载模板文件
264             location.href = apiUrl + 'AccountChannelAccess/DownloadFileTemplate?type=' + type;
265         }
266     </script>
267 </body>
268 </html>

HTML
WebAPI接口代码

复制代码
  1 using Microsoft.Xrm.Sdk;
  2 using Microsoft.Xrm.Sdk.Query;
  3 using Newtonsoft.Json;
  4 using NPOI.OpenXml4Net.OPC.Internal;
  5 using System;
  6 using System.Collections.Generic;
  7 using System.Data.Entity.Core.Objects.DataClasses;
  8 using System.IO;
  9 using System.Linq;
 10 using System.Net;
 11 using System.Net.Http;
 12 using System.Net.Http.Headers;
 13 using System.Reflection;
 14 using System.Text;
 15 using System.Threading.Tasks;
 16 using System.Web;
 17 using System.Web.Http;
 18 using ZZ_Common_API.Crm;
 19 using ZZ_Common_API.Models.QDRWModels;
 20 using EntityReference = Microsoft.Xrm.Sdk.EntityReference;
 21 
 22 namespace ZZ_Common_API.Controllers.QDRWControllers
 23 {
 24     /// <summary>
 25     /// 附件相关WebAPI接口
 26     /// </summary>
 27     [RoutePrefix("AccountChannelAccess")]
 28     public class AccountChannelAccessController : ApiController
 29     {
 30         #region 下载模板文件 strat
 31         /// <summary>
 32         /// 下载附件
 33         /// </summary>
 34         /// <param name="type">1商业计划书新签,2商业计划书续签,3渠道基本信息评价表,4海外网络退出要素确认表</param>
 35         /// <returns></returns>
 36         [Route("DownloadFileTemplate")]
 37         [HttpGet]
 38         public async Task<dynamic> DownloadFileTemplate(int type) 
 39         {
 40             HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
 41             string fileName = string.Empty;
 42             switch (type)
 43             {
 44                 case 1:
 45                     fileName = "附件1:商业计划书新签(可下载模版).pptx";
 46                     break;
 47                 case 2:
 48                     fileName = "附件2:商业计划书续签(可下载模版).pptx";
 49                     break;
 50                 case 3:
 51                     fileName = "附件3:渠道基本信息评价表(可下载模版).docx";
 52                     break;
 53                 case 4:
 54                     fileName = "附件4:海外网络退出要素确认表(可下载模板).docx";
 55                     break;
 56                 default:
 57                     fileName = "附件1:商业计划书新签(可下载模版).pptx";
 58                     break;
 59             }
 60            
 61             var filePath = AppDomain.CurrentDomain.BaseDirectory + @"\Files\Template\" + fileName;
 62             if (!File.Exists(filePath))
 63             {
 64                 return ResponseMessage(new HttpResponseMessage()
 65                 {
 66                     Content =
 67                      new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下载失败,未能找到文件-{fileName}的路径" })),
 68                        Encoding.GetEncoding("UTF-8"), "application/json"),
 69                     StatusCode = HttpStatusCode.NoContent
 70                 });
 71             }
 72             var stream = new FileStream(filePath, FileMode.Open);
 73             responseMessage.Content = new StreamContent(stream);
 74             responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
 75             HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(fileName));
 76             return ResponseMessage(responseMessage);
 77         }
 78         
 79 
 80         #endregion 下载模板文件 end
 81 
 82         #region 上传附件 start
 83         /// <summary>
 84         /// 使用系统的注释Annotation  添加附件
 85         /// </summary>
 86         /// <returns></returns>
 87         [Route("SaveAnnotation")]
 88         [HttpPost]
 89         public object SaveAnnotation()
 90         {
 91             try
 92             {
 93                 HttpFileCollection fileCollection = HttpContext.Current.Request.Files;
 94                 if (fileCollection.Count <= 0)
 95                 {
 96                     return Json(new { code = 0, msg = $"请先选择文件后点击上传" });
 97                 }
 98                 var formData = HttpContext.Current.Request.Form;
 99                 var entityId = formData["entityId"];
100                 var entityName = formData["entityName"];
101 
102                 Guid objId = Guid.Empty;
103                 if (!Guid.TryParse(entityId, out objId)) //检验实体id是否合法
104                 {
105                     return Json(new { code = 0, msg = $"上传失败,entityId是不合法的GUID:{entityId}" });
106                 }
107                 HttpPostedFile formFile = fileCollection[0];
108                 if (formFile.ContentLength > 1048576000)//检查文件大小
109                 {
110                     return Json(new { code = 0, msg = $"{formFile.FileName}文件大小不得超过{1048576000 / (1024f * 1024f)}M" });//请求体过大,文件大小超标
111                 }
112                 var suffix = Path.GetExtension(formFile.FileName);//提取上传的文件文件后缀
113                 if (".js;.bat;.exe;.sh".IndexOf(suffix) > 0) //检查文件格式
114                 {
115                     return Json(new { code = 0, msg = $"不支持此文件类型-{suffix}" });//类型不正确
116                 }
117 
118                 AnnotationModel model = new AnnotationModel();
119                 model.Subject = Path.GetFileName(formFile.FileName).Substring(1);
120                 model.NoteText = "";
121                 model.ObjectId = objId;
122                 model.ObjectIdName = entityName;
123                 model.MimeType = formFile.ContentType;
124                 model.FileSize = formFile.ContentLength;
125                 model.FileName = Path.GetFileName(formFile.FileName);
126                 model.DocumentBody = Convert.ToBase64String(GetFileByte(formFile));
127                 //保存到Annotation中
128                 CreateAnnotation(model);
129 
130                 return Json(new { code = 1, msg = "上传成功" });
131             }
132             catch (Exception ex)
133             {
134                 return Json(new { code = 0, msg = $"上传失败 + {ex.Message}" });
135             }
136         }
137         #endregion 上传附件 end
138 
139         #region 下载附件 start
140         /// <summary>
141         /// 下载附件
142         /// </summary>
143         /// <param name="id">附件id</param>
144         /// <returns></returns>
145         [Route("DownloadAnnotation")]
146         [AcceptVerbs("GET")]
147         public IHttpActionResult DownloadAnnotation(string id)
148         {
149             //通过附件id 找到附件 然后转化附件字节流 填入到响应中
150             HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
151             try
152             {
153                 if (string.IsNullOrEmpty(id))
154                 {
155                     return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone));
156                 }
157                 Tuple<string, Stream> tuple = GetAnnotationStream(id);
158                 if (tuple.Item1 == "未能获取到字节流")
159                 {
160                     return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone));
161                 }
162                 responseMessage.Content = new StreamContent(tuple.Item2);
163                 responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
164                 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(tuple.Item1));
165             }
166             catch (Exception ex)
167             {
168                 return ResponseMessage(new HttpResponseMessage()
169                 {
170                     Content =
171                      new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下载失败-{ex.Message}" })),
172                        Encoding.GetEncoding("UTF-8"), "application/json"),
173                     StatusCode = HttpStatusCode.NoContent
174                 });
175             }
176             return ResponseMessage(responseMessage);
177         }
178         #endregion 下载附件 end
179 
180         #region 根据实体id获取实体数据下相关所有附件,打包成zip文件下载 start
181         /// <summary>
182         /// 下载实体数据下所有的附件zip
183         /// 根据实体id获取实体数据下相关所有附件,打包成zip文件下载
184         /// </summary>
185         /// <param name="id"></param>
186         /// <returns></returns>
187         [Route("DownloadAnnotationAll")]
188         [AcceptVerbs("GET")]
189         public IHttpActionResult DownloadAnnotationAll(string id)
190         {
191             HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
192             try
193             {
194                 if (string.IsNullOrEmpty(id))
195                 {
196                     return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone));
197                 }
198                 //通过模块id 找到相关的所有附件 然后将所有的附件打包成zip文件 将zip文件转化字节流 填入到响应流中
199                 if (string.IsNullOrEmpty(id))
200                 {
201                     return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone));
202                 }
203                 Tuple<string, Stream> tuple = GetAllAnnotationStream(id);
204                 if (tuple.Item1 == "未能获取到字节流")
205                 {
206                     return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Gone));
207                 }
208                 responseMessage.Content = new StreamContent(tuple.Item2);
209                 responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
210                 HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlPathEncode(tuple.Item1));
211             }
212             catch (Exception ex)
213             {
214                 return ResponseMessage(new HttpResponseMessage()
215                 {
216                     Content =
217                      new StringContent(JsonConvert.SerializeObject(Json(new { code = 0, msg = $"下载失败-{ex.Message}" })),
218                        Encoding.GetEncoding("UTF-8"), "application/json"),
219                     StatusCode = HttpStatusCode.NoContent
220                 });
221             }
222             return ResponseMessage(responseMessage);
223         }
224 
225         #endregion 根据实体id获取实体数据下相关所有附件,打包成zip文件下载 end
226 
227         #region API专用 删除附件
228         /// <summary>
229         /// 删除附件
230         /// </summary>
231         /// <param name="id">附件ids</param>
232         /// <returns></returns>
233         [Route("DelAnnotation")]
234         [HttpPost]
235         public object DelAnnotation([FromBody] string id)
236         {
237             try
238             {
239                 if (string.IsNullOrEmpty(id))
240                 {
241                     return Json(new { code = 0, msg = $"删除附件时id不能为空" });
242                 }
243                 DeleteAnnotation(id);
244                 return Json(new { code = 1, msg = "删除成功" }); ;
245             }
246             catch (Exception ex)
247             {
248                 return Json(new { code = 0, msg = $"删除失败:{ex.Message}" });
249             }
250         }
251         #endregion API专用 删除附件
252 
253         #region 内部方法 start
254 
255         #region 上传附件存到Annotation中 start
256         /// <summary>
257         /// 将上传文件对象转化成byte字节流
258         /// </summary>
259         /// <param name="httpPostedFile">上传文件</param>
260         /// <returns></returns>
261         private byte[] GetFileByte(HttpPostedFile httpPostedFile)
262         {
263             byte[] bytes = new byte[httpPostedFile.ContentLength];
264             using (BinaryReader reader = new BinaryReader(httpPostedFile.InputStream, Encoding.UTF8))
265             {
266                 bytes = reader.ReadBytes(bytes.Length);
267             }
268             return bytes;
269         }
270         /// <summary>
271         /// 创建附件数据
272         /// </summary>
273         /// <param name="annotation"></param>
274         /// <returns></returns>
275         private Guid CreateAnnotation(AnnotationModel annotation)
276         {
277             Guid guid = Guid.Empty;
278             IOrganizationService orgService = OrgServiceUtil.Client;
279             Entity entity = new Entity("annotation");
280             entity["filename"] = annotation.FileName;
281             entity["subject"] = annotation.Subject;
282             entity["mimetype"] = annotation.MimeType;
283             entity["filesize"] = annotation.FileSize;
284             entity["documentbody"] = annotation.DocumentBody;
285             entity["objectid"] = new EntityReference(annotation.ObjectIdName, annotation.ObjectId);
286             guid = orgService.Create(entity);
287             return guid;
288         }
289         #endregion 上传附件存到Annotation中 end
290 
291 
292         #region 根据附件id获取附件字节流 start
293         /// <summary>
294         /// 根据附件id获取附件字节流
295         /// </summary>
296         /// <param name="annotationid">annotationid</param>
297         /// <returns></returns>
298         public Tuple<string, Stream> GetAnnotationStream(string annotationid)
299         {
300             Tuple<string, Stream> rr = new Tuple<string, Stream>("未能获取到字节流", null);
301             try
302             {
303                 IOrganizationService orgService = OrgServiceUtil.Client;
304                 ColumnSet cols = new ColumnSet("filename", "documentbody");
305                 Entity entity = orgService.Retrieve("annotation", new Guid(annotationid), cols);
306                 string documentbody = entity.GetAttributeValue<string>("documentbody");
307                 string filename = entity.GetAttributeValue<string>("filename");
308                 byte[] fileContent = Convert.FromBase64String(documentbody);
309                 Stream fileStream = new MemoryStream(fileContent);
310                 rr = new Tuple<string, Stream>(filename, fileStream);
311             }
312             catch (Exception)
313             {
314                 return rr;
315             }
316             return rr;
317         }
318         #endregion 根据附件id获取附件字节流 end
319 
320         #region 根据实体数据id获取所有附件并打包成zip文件,然后转换成字节流 start
321         /// <summary>
322         /// 根据实体数据id获取所有附件达成zip包后转换成字节流
323         /// </summary>
324         /// <param name="entityId">实体数据id</param>
325         /// <returns></returns>
326         public Tuple<string, Stream> GetAllAnnotationStream(string entityId)
327         {
328             Tuple<string, Stream> rr = new Tuple<string, Stream>("未能获取到字节流", null);
329             try
330             {
331                 IOrganizationService orgService = OrgServiceUtil.Client;
332                 //1.根据实体数据id查询出所有相关得附件
333                 var dbList = GetDbFileByEntityId(orgService,new Guid(entityId));
334                 //2.遍历所有相关得附件 将各个附件流转化存放到内存字典中
335                 Dictionary<string, byte[]> dic = new Dictionary<string, byte[]>();
336                 foreach (var cc in dbList)
337                 {
338                     string documentbody = cc.GetAttributeValue<string>("documentbody");
339                     string filename = cc.GetAttributeValue<string>("filename");
340                     byte[] fileContent = Convert.FromBase64String(documentbody);
341                     Tuple<string, byte[]> tt = new Tuple<string, byte[]>(filename, fileContent);
342                     if (tt.Item2 == null)
343                     {
344                         continue;
345                     }
346                     dic.Add(tt.Item1, tt.Item2);
347                 }
348                 if (dic.Count > 0)
349                 {
350                     //3.将多个字节流从字典压缩成zip文件并转成一个字节流
351                     Stream stream = ZipByteHelper.SetbytesToZipStream2(dic);
352                     rr = new Tuple<string, Stream>("附件.zip", stream);
353                 }
354             }
355             catch (Exception)
356             {
357                 return rr;
358             }
359             return rr;
360         }
361 
362         private List<Entity> GetDbFileByEntityId(IOrganizationService orgService,Guid entityId) 
363         {
364             var list = new List<Entity>();
365             QueryExpression query = new QueryExpression("annotation");
366             query.ColumnSet = new ColumnSet(true);
367             query.Criteria.AddCondition(new ConditionExpression("objectid", ConditionOperator.Equal, entityId));
368             var datas = orgService.RetrieveMultiple(query);
369             list = datas.Entities.ToArray().ToList();
370             return list;
371         }
372         #endregion 根据实体数据id获取所有附件并打包成zip文件,然后转化称字节流 end
373 
374         #region 删除附件 start
375         /// <summary>
376         /// 删除附件注释
377         /// </summary>
378         /// <param name="annotationid">附件id</param>
379         /// <returns></returns>
380         /// <exception cref="Exception"></exception>
381         public bool DeleteAnnotation(string annotationid)
382         {
383             bool result = true;
384             if (string.IsNullOrEmpty(annotationid))
385             {
386                 result = false;
387             }
388             IOrganizationService orgService = OrgServiceUtil.Client;
389             orgService.Delete("annotation", Guid.Parse(annotationid));
390             return result;
391         }
392         #endregion 删除附件 end
393         #endregion 内部方法 end
394     }
395 }

webAPI接口
拓展Zip流压缩帮助类

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Linq;
 5 using System.Web;
 6 
 7 namespace ZZ_Common_API.Utilities
 8 {
 9     /// <summary>
10     /// 压缩、解压帮助类
11     /// </summary>
12     public static class ZipByteHelper
13     {
14         /// <summary>
15         /// 将多个文件流转化成zip文件流
16         /// </summary>
17         /// <param name="dic"></param>
18         /// <returns></returns>
19         public static Stream SetbytesToZipStream2(Dictionary<string, byte[]> dic)
20         {
21             byte[] buffer = new byte[6500];
22             MemoryStream returnStream = new MemoryStream();
23             var zipMs = new MemoryStream();
24             using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(zipMs))
25             {
26                 zipStream.SetLevel(9);//设置 压缩等级 (9级 500KB 压缩成了96KB)
27                 foreach (var kv in dic)
28                 {
29                     string fileName = kv.Key;
30                     using (var streamInput = new MemoryStream(kv.Value))
31                     {
32                         zipStream.PutNextEntry(new ICSharpCode.SharpZipLib.Zip.ZipEntry(fileName));
33                         while (true)
34                         {
35                             var readCount = streamInput.Read(buffer, 0, buffer.Length);
36                             if (readCount > 0)
37                             {
38                                 zipStream.Write(buffer, 0, readCount);
39                             }
40                             else
41                             {
42                                 break;
43                             }
44                         }
45                         zipStream.Flush();
46                     }
47                 }
48                 zipStream.Finish();
49                 zipMs.Position = 0;
50                 zipMs.CopyTo(returnStream, 5600);
51             }
52             returnStream.Position = 0;
53 
54             return returnStream;
55         }
56     }
57 }

ZIP帮助类

相关推荐
乌云大帝11 天前
IIS服务器部署C# WebApi程序,客户端PUT,DELETE请求无法执行
运维·服务器·c#·webapi
csdn_aspnet20 天前
在.NET 6中使用Serilog收集日志
webapi·.net6.0
陈逸子风24 天前
(系列十三)Vue3+Echarts搭建超好看的系统面板
vue3·webapi·权限·流程·表单
陈逸子风1 个月前
(系列十二)Vue3+.Net8实现用户登录(超详细登录文档)
vue3·webapi·权限·流程·表单
arbboter1 个月前
libcurl.net入门使用
c#·.net·curl·webapi·libcurl·libcurl.net
arbboter1 个月前
RestSharp基本使用方法
开发语言·c#·winform·curl·webapi·restsharp
陈逸子风1 个月前
(系列十一)Vue3框架中路由守卫及请求拦截(实现前后端交互)
vue3·webapi·权限·流程·表单
csdn_aspnet2 个月前
.NET 8 Web API 中的身份验证和授权
webapi·.net8.0
csdn_aspnet2 个月前
在 .NET 8 Web API 中实现 Entity Framework 的 Code First 方法
webapi·.net8.0
csdn_aspnet2 个月前
.NET 8 中的 Mini WebApi
webapi·.net8.0