我在服务器上使用GridFS存储Word(.docx)文件。我希望能够使用docx-builder NPM包将文档合并到一个Word文件中。
以下是我上传文件的方式:
Meteor.methods({
uploadFiles: function (files) {
check(files, [Object]);
if (files.length < 1)
throw new Meteor.Error("invalid-files", "No files were uploaded");
var documentPaths = [];
_.each(files, function (file) {
ActivityFiles.insert(file, function (error, fileObj) {
if (error) {
console.log("Could not upload file");
} else {
documentPaths.push("/cfs/files/activities/" + fileObj._id);
}
});
});
return documentPaths;
}
})
如何在服务器端执行此操作?我只能做这个服务器端,因为我使用的软件包需要fs
软件包,而不能在客户端执行。
此处我正在努力解决这个问题。我从客户端调用以下方法(声明为Meteor.method
):
print: function(programId) {
// Get the program by ID.
var program = Programs.findOne(programId);
// Create a new document.
var docx = new docxbuilder.Document();
// Go through all activities in the program.
program.activityIds.forEach(function(activityId) {
// Create a temporary server side folder to store activity files.
const tempDir = fs.mkdtempSync('/tmp/');
// Get the activity by ID.
var activity = Activities.findOne(activityId);
// Get the document by ID.
var document = ActivityFiles.findOne(activity.documents.pop()._id);
// Declare file path to where file will be read.
const filePath = tempDir + sep + document.name();
// Create stream to write to path.
const fileStream = fs.createWriteStream(filePath);
// Read from document, write to file.
document.createReadStream().pipe(fileStream);
// Insert into final document when finished writinf to file.
fileStream.on('finish', () => {
docx.insertDocxSync(filePath);
// Delete file when operation is completed.
fs.unlinkSync(filePath);
});
});
// Save the merged document.
docx.save('/tmp' + sep + 'output.docx', function (error) {
if (error) {
console.log(error);
}
// Insert into Collection so client can access merged document.
Fiber = Npm.require('fibers');
Fiber(function() {
ProgramFiles.insert('/tmp' + sep + 'output.docx');
}).run();
});
}
但是,当我从客户端的ProgramFiles
集合下载最终文档时,该文档是一个空的Word文档。
这里出了什么问题?
我已将@ FrederickStark的答案纳入我的代码中。现在就停留在这个部分。
这是另一次尝试:
'click .merge-icon': (e) => {
var programId = Router.current().url.split('/').pop();
var programObj = Programs.findOne(programId);
var insertedDocuments = [];
programObj.activityIds.forEach(function(activityId) {
var activityObj = Activities.findOne(activityId);
var documentObj = ActivityFiles.findOne(activityObj.documents.pop()._id);
JSZipUtils.getBinaryContent(documentObj.url(), callback);
function callback(error, content) {
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
var xml = zip.files[doc.fileTypeConfig.textPath].asText();
xml = xml.substring(xml.indexOf("<w:body>") + 8);
xml = xml.substring(0, xml.indexOf("</w:body>"));
xml = xml.substring(0, xml.indexOf("<w:sectPr"));
insertedDocuments.push(xml);
}
});
JSZipUtils.getBinaryContent('/assets/template.docx', callback);
function callback(error, content) {
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
console.log(doc);
setData(doc);
}
function setData(doc) {
doc.setData({
// Insert blank line between contents.
inserted_docs_formatted: insertedDocuments.join('<w:br/><w:br/>')
// The template file must use a `{@inserted_docs_formatted}` placeholder
// that will be replaced by the above value.
});
doc.render();
useResult(doc);
}
function useResult(doc) {
var out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
saveAs(out, 'output.docx');
}
答案 0 :(得分:3)
查看docx-builder
的文档,它只支持从文件系统中读取docx文件。调用document.url()
的问题在于它为您提供了一个可以通过http访问的URL,而不是文件系统上的路径。
因此,要使用GridFS,您首先需要将文件写入临时文件夹,然后docx-builder
才能读取它们。
import fs from 'fs';
import { sep } from 'path';
const tempDir = fs.mkdtempSync('/tmp/' + sep);
program.activityIds.forEach(function(activityId) {
var activity = Activities.findOne(activityId);
console.log(activity);
var document = ActivityFiles.findOne(activity.documents.pop()._id);
documents.push(document);
// Build a file path in the temp folder
const filePath = tempDir + sep + document.name();
// Write the document to the file system using streams
const fileStream = fs.createWriteStream(filePath);
document.createReadStream().pipe(fileStream);
// When the stream has finished writing the file, add it to your docx
fileStream.on('finish', () => {
console.log(filePath);
docx.insertDocxSync(filePath);
// Delete the file after you're done
fs.unlinkSync(filePath);
});
});
我怀疑你可以使用fs.writeFileSync(filePath, document.data)
同步执行此操作,但不确定,所以在示例中没有使用它。
或者,您可以查找可以支持从流或缓冲区读取的docx包,然后您将不需要临时文件。
答案 1 :(得分:3)
我只能做这个服务器端,因为我使用的软件包需要
fs
软件包,而不能在客户端执行。
虽然您当前使用的docx-builder
库依赖于fs
(因此在Node环境中),但可以直接使用其依赖项docxtemplater
并使用它它只在客户端(浏览器)端。
非常像docx-builder
一样,你可以从标准开始#34;模板文件,您可以将本地docx文件合并到其中,然后生成生成的docx文件并将其保存在本地或发送到您的服务器。
实现您要做的事情(即合并docx文件)的基本步骤,但在客户端将是:
<input type="file" id="fileInput" />
docx-builder
中所述:var insertedDocsFormatted = [];
// If the file is locally provided through File type input:
document.getElementById('fileInput').addEventListener('change', function (evt) {
var file = evt.currentTarget.files[0],
fr = new FileReader();
fr.onload = function () {
insertDocContent(fr.result);
};
fr.readAsArrayBuffer(file);
});
// Or if the file already exists on a server:
JSZipUtils.getBinaryContent(url, function (err, content) {
insertDocContent(content);
});
function insertDocContent(content) {
var zip = new JSZip(content),
doc = new Docxtemplater().loadZip(zip);
var xml = zip.files[doc.fileTypeConfig.textPath].asText();
// Inspired from https://github.com/raulbojalil/docx-builder
xml = xml.substring(xml.indexOf("<w:body>") + 8);
xml = xml.substring(0, xml.indexOf("</w:body>"));
xml = xml.substring(0, xml.indexOf("<w:sectPr"));
// Keep for later use.
insertedDocsFormatted.push(xml);
}
// 'template.docx' is a static file on your server (e.g. in `public/` folder)
// But it could even be replaced by a user locally provided file,
// as done in step 2 above.
JSZipUtils.getBinaryContent('template.docx', callback);
function callback(err, content) {
var zip = new JSZip(content);
var doc = new Docxtemplater().loadZip(zip);
setData(doc);
}
function setData(doc) {
doc.setData({
// Insert blank line between contents.
inserted_docs_formatted: insertedDocsFormatted.join('<w:br/><w:br/>')
// The template file must use a `{@inserted_docs_formatted}` placeholder
// that will be replaced by the above value.
});
doc.render();
useResult(doc);
}
function useResult(doc) {
var out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
});
saveAs(out, 'output.docx');
}
在线演示:https://ghybs.github.io/docx-builder-demo/(根本没有服务器端处理,纯客户端逻辑)
源代码:https://github.com/ghybs/docx-builder-demo
库: