Rails 7在Windows环境下自定义Doc文件下载失效解决方案问询
Rails 7自定义Doc文件下载在Windows系统内容为空的问题
问题描述
在Rails 7中实现了前端自定义Doc文件下载功能,点击按钮触发downloadDoc()函数生成包含表单字段、图片的Doc文件。该功能在非Windows设备上正常,但Windows系统中下载后的文件内容为空。尝试过多个Rails gem,均只能生成纯文本内容,无法插入图片。
原代码
downloadDoc() { // Cif Inputs var labelsCont = document.getElementsByClassName("cif_form_export_filed_labels") var valuesCont = document.getElementsByClassName("cif_form_export_filed_values") var labels = [] var values = [] var allFields = "" Object.values(labelsCont).map(el => labels.push(el.value || "")) Object.values(valuesCont).map(el => values.push(el.value || "")) labels.map((l, i) => { allFields += `<h4>${l} - ${values[i]}</h4>` }) // map photos var allPhotos = "" const a = document.getElementsByClassName('cif_photos') var photos = [] Object.values(a).map(el => { photos.push(JSON.parse(el.value)); }) const groupedMap = photos.reduce( (aa, e) => aa.set(e.photo_type, [...aa.get(e.photo_type) || [], e]), new Map() ); groupedMap.forEach((v, k) => { allPhotos += `<h4>${k}</h4>` v.map(img => { allPhotos += `<h5>${img.location}</h5>` allPhotos += `<img src=${img.url} width=200px height=200px > ` }) }) // download process var preHtml = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>Export HTML To Doc</title></head><body>"; var postHtml = "</body></html>"; var html = preHtml + postHtml + "<h2>CIF Fields</h2>" + allFields + "<h2>CIF Photos</h2>" + allPhotos + "<h2>Summary</h2>"; var blob = new Blob(['\ufeff', html], { type: 'application/msword' }); var url = 'data:application/vnd.ms-word;charset=utf-8,' + encodeURIComponent(html); var filename = 'document.docx'; var downloadLink = document.createElement("a"); document.body.appendChild(downloadLink); if (navigator.msSaveOrOpenBlob) { navigator.msSaveOrOpenBlob(blob, filename); } else { downloadLink.href = url; downloadLink.download = filename; downloadLink.click(); } }
问题原因
- HTML结构错误:原代码将内容拼接到
preHtml + postHtml之后,导致实际内容位于</body></html>标签外部。非Windows浏览器能容错解析,但Windows下的Microsoft Word对HTML结构要求严格,会忽略标签外的内容,造成文件为空。 - 文件类型不匹配:使用
application/msword(旧版doc格式),但文件名是.docx,格式与扩展名不匹配,影响Word解析。 - 编码与Blob处理:Windows的
navigator.msSaveOrOpenBlob接口对Blob内容的结构和编码兼容性要求更高,错误的HTML结构会导致内容无法被识别。
修正后的代码
downloadDoc() { // Cif Inputs var labelsCont = document.getElementsByClassName("cif_form_export_filed_labels") var valuesCont = document.getElementsByClassName("cif_form_export_filed_values") var labels = [] var values = [] var allFields = "" Object.values(labelsCont).map(el => labels.push(el.value || "")) Object.values(valuesCont).map(el => values.push(el.value || "")) labels.map((l, i) => { allFields += `<h4>${l} - ${values[i]}</h4>` }) // map photos var allPhotos = "" const a = document.getElementsByClassName('cif_photos') var photos = [] Object.values(a).map(el => { photos.push(JSON.parse(el.value)); }) const groupedMap = photos.reduce( (aa, e) => aa.set(e.photo_type, [...aa.get(e.photo_type) || [], e]), new Map() ); groupedMap.forEach((v, k) => { allPhotos += `<h4>${k}</h4>` v.map(img => { allPhotos += `<h5>${img.location}</h5>` allPhotos += `<img src="${img.url}" width="200px" height="200px" /> ` }) }) // download process - 修正HTML结构,将内容放在<body>内部 var preHtml = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>Export HTML To Doc</title></head><body>"; var contentHtml = "<h2>CIF Fields</h2>" + allFields + "<h2>CIF Photos</h2>" + allPhotos + "<h2>Summary</h2>"; var postHtml = "</body></html>"; var html = preHtml + contentHtml + postHtml; // 匹配docx格式的MIME类型 const mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; var blob = new Blob(['\ufeff', html], { type: mimeType }); var filename = 'document.docx'; var downloadLink = document.createElement("a"); document.body.appendChild(downloadLink); if (navigator.msSaveOrOpenBlob) { navigator.msSaveOrOpenBlob(blob, filename); } else { // 使用URL.createObjectURL替代data URL,避免编码问题 downloadLink.href = URL.createObjectURL(blob); downloadLink.download = filename; downloadLink.click(); // 释放URL对象 setTimeout(() => URL.revokeObjectURL(downloadLink.href), 100); } document.body.removeChild(downloadLink); }
关键修改点
- 调整HTML拼接顺序,将内容放在
<body>标签内部,确保Word能正确识别内容。 - 更换MIME类型为
application/vnd.openxmlformats-officedocument.wordprocessingml.document,与.docx扩展名匹配。 - 使用
URL.createObjectURL替代data:URL,避免encodeURIComponent带来的编码问题,同时优化内存使用(添加URL释放逻辑)。 - 修正图片标签的属性引号,符合标准HTML语法,提升兼容性。
内容的提问来源于stack exchange,提问作者Soumik Maity
相关产品推荐
相关产品推荐

