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>
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 }
拓展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 }