Create and download Word file in Blazor - ms-word

I am trying to create a Word file and download the created file in clients browser.
The creation part seems to work fine and I can open the file manually from its Folder.
But the downloaded file in browser does not open correctly and produces an error
"The file is corrupt and cannot be opened"
I am using the code from here
Microsoft instructions for downloading a file in Blazor
My code seems like this
private async Task CreateAndDownloadWordFile()
{
var destination = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = destination + "\\test12.docx";
//SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(destination, SpreadsheetDocumentType.Workbook);
using (WordprocessingDocument doc = WordprocessingDocument.Create
(fileName, DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
{
// Add a main document part.
MainDocumentPart mainPart = doc.AddMainDocumentPart();
// Create the document structure and add some text.
mainPart.Document = new Document();
Body body = mainPart.Document.AppendChild(new Body());
Paragraph para = body.AppendChild(new Paragraph());
Run run = para.AppendChild(new Run());
// String msg contains the text, "Hello, Word!"
run.AppendChild(new Text("New text in document"));
}
var fileStream = GetFileStream();
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
private Stream GetFileStream()
{
var randomBinaryData = new byte[50 * 1024];
var fileStream = new MemoryStream(randomBinaryData);
return fileStream;
}
And I use this Javascript code
async function downloadFileFromStream(fileName, contentStreamReference) {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
triggerFileDownload(fileName, url);
URL.revokeObjectURL(url);
}
function triggerFileDownload(fileName, url) {
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = fileName ?? '';
anchorElement.click();
anchorElement.remove();
}
Any ideas?

But the downloaded file in browser does not open correctly
That is probably because you
First create a Word document
And then download var randomBinaryData = new byte[50 * 1024];
the downloaded file in browser
Check those. Are they exactly 50 * 1024 bytes ?
--
Also, you shouldn't pass the full C:\... path to the download funtion.
var fileStream = File.OpenRead(filename);
using var streamRef = new DotNetStreamReference(stream: fileStream);
//await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
await JS.InvokeVoidAsync("downloadFileFromStream", "suggestedName", streamRef);

Related

How to allow users to upload files with Google Form without login?

Where can I find code and instruction on how to allow users to upload files with Google Form without login?
I searched all over here and couldn't find any information.
https://developers.google.com/apps-script/reference
Thanks in advance.
The user will be uploading the files to your drive. So, google needs to verify the user. If there is no verification, someone can fill your drive in no time.
It is for your safety to know who has uploaded, so, login is must.
There's a workaround, I'm in a hurry to write the code now, but if you're interested let me know and I'll edit later.
Basically, you set up a web app with apps script, then you setup a custom HTML form, you'll have to manually collect the file, convert is to base64 then json, then when you catch it in apps script you reverse the process and save it wherever you want in your drive.
Since the user will be executing the script as you, there's no verification required
/*
These functions basically go through a file array and reads the files first as binary string (in second function), then converts the files to base64 string (func 1) before stringifying the files (after putting their base64 content into an object with other metadata attached; mime, name e.t.c);
You pass this stringified object into the body part of fetch(request,{body:"stringified object goes here"})
see next code block for how to read in apps script and save the files to google drive
N.B. The body data will be available under doPost(e){e.postData.contents}
*/
async function bundleFilesForUpload(){
let filesDataObj = [];
let copy = {fileInfo:{"ogname":"","meme":""},fileData:""};
for(let i = 0 ; i < counters.localVar.counters.filesForUploadArr.length ; i++){
let tempObj = JSON.parse(JSON.stringify(copy));
let file = counters.localVar.counters.filesForUploadArr[i];
tempObj.fileInfo.ogname = file.name;
tempObj.fileInfo.meme = file.type;
tempObj.fileData = await readFile(file).then((file)=>{
file = btoa(file);
return file;
}).then((file)=>{
return file;
})
filesDataObj.push(tempObj);
}
return filesDataObj;
}
async function readFile (file){
const toBinaryString = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
let parsedFile = null;
parsedFile = await toBinaryString(file);
return parsedFile;
}
/*From doPost downward, we read the file Array convert the base64 to blob and make a file in google drive using the blob and metadata we have, you may also see some sheet code, I'm using sheet as db for this */
//in buit function doPost in Code.gs
doPost(e){
const myDataObj = JSON.parse(e.postData.contents);
mainFileFunc(myDataObj.params[0].dataObj.images);
//the actual object structure might look different from yours, console log around
}
function mainFileFunc(fileArr) {
let myArrObj = [{"madeit":"toFileF"}];
let copy = JSON.parse(JSON.stringify(myArrObj[0]));
//sheet.getRange("A1").setValue(JSON.stringify(fileArr.length));
for(let i=0 ; i < fileArr.length ; i++){
myArrObj.push(copy);
let blob = doFileStuff(fileArr[i].data,fileArr[i].info[0].mime,fileArr[i].id);
myArrObj[i] = uploadFileOne(blob,fileArr[i].id);
myArrObj[i].mime = fileArr[i].info[0].mime;
myArrObj[i].realName = fileArr[i].name;
// sheet.getRange("A"+(i+1)).setValue(myArrObj[i].name);
// sheet.getRange("B"+(i+1)).setValue(myArrObj[i].url);
// sheet.getRange("C"+(i+1)).setValue(myArrObj[i].mime);
// sheet.getRange("D"+(i+1)).setValue(myArrObj[i].size);
}
return myArrObj;
}
function doFileStuff(filedata,filetype,filename){
var data = Utilities.base64Decode(filedata, Utilities.Charset.UTF_8);
var blob = Utilities.newBlob(data,filetype,filename);
return blob;
}
function uploadFileOne(data,filename) {
let myObj = {}
myObj["name"] = "";
myObj["realName"] = "Story_Picture";
myObj["url"] = "";
myObj["mime"] = "";
myObj["size"] = "";
myObj["thumb"] = "nonety";
var folders = DriveApp.getFoldersByName("LadhaWeb");
while (folders.hasNext()) {
var folder = folders.next();
folder.createFile(data);
}
var files = DriveApp.getFilesByName(filename);
while (files.hasNext()) {
var file = files.next();
myObj.name = file.getName();
myObj.url = file.getUrl();
myObj.mime = file.getMimeType();
myObj.size = file.getSize();
}
return myObj;
}
You can view the full frontend code for this project here and the backend here.
Hope this helps someone.

Word found unreadable content in xxx.docx after split a docx using openxml

I have a full.docx which includes two math questions, the docx embeds some pictures and MathType equation (oleobject), I split the doc according to this, get two files (first.docx, second.docx) , first.docx works fine, the second.docx, however, pops up a warning dialog when I try to open it:
"Word found unreadable content in second.docx. Do you want to recover the contents of this document? If you trust the source of this document, click Yes."
After click "Yes", the doc can be opened, the content is also correct, I want to know what is wrong with the second.docx? I have checked it with "Open xml sdk 2.5 productivity tool", but found no reason. Very appreciated for any help. Thanks.
The three files have been uploaded to here.
Show some code:
byte[] templateBytes = System.IO.File.ReadAllBytes(TEMPLATE_YANG_FILE);
using (MemoryStream templateStream = new MemoryStream())
{
templateStream.Write(templateBytes, 0, (int)templateBytes.Length);
string guidStr = Guid.NewGuid().ToString();
using (WordprocessingDocument document = WordprocessingDocument.Open(templateStream, true))
{
document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
MainDocumentPart mainPart = document.MainDocumentPart;
mainPart.Document = new Document();
Body bd = new Body();
foreach (DocumentFormat.OpenXml.Wordprocessing.Paragraph clonedParagrph in lst)
{
bd.AppendChild<DocumentFormat.OpenXml.Wordprocessing.Paragraph>(clonedParagrph);
clonedParagrph.Descendants<Blip>().ToList().ForEach(blip =>
{
var newRelation = document.CopyImage(blip.Embed, this.wordDocument);
blip.Embed = newRelation;
});
clonedParagrph.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().ToList().ForEach(imageData =>
{
var newRelation = document.CopyImage(imageData.RelationshipId, this.wordDocument);
imageData.RelationshipId = newRelation;
});
}
mainPart.Document.Body = bd;
mainPart.Document.Save();
}
string subDocFile = System.IO.Path.Combine(this.outDir, guidStr + ".docx");
this.subWordFileLst.Add(subDocFile);
File.WriteAllBytes(subDocFile, templateStream.ToArray());
}
the lst contains Paragraph cloned from original docx using:
(DocumentFormat.OpenXml.Wordprocessing.Paragraph)p.Clone();
Using productivity tool, found oleobjectx.bin not copied, so I add below code after copy Blip and ImageData:
clonedParagrph.Descendants<OleObject>().ToList().ForEach(ole =>
{
var newRelation = document.CopyOleObject(ole.Id, this.wordDocument);
ole.Id = newRelation;
});
Solved the issue.

file not downloading properly

I am downloading a file from a url and saving it to a directory on my phone.
the path is: /private/var/mobile/Applications/17E4F0B0-0781-4259-B39D-37057D44B778/Documents/samplefile.txt
However, when i debug the file is created and downloaded. But, when i ad-hoc it and run the file. samplefile.txt is created but it's blank.
Code:
String directory = Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine (directory, "samplefile.txt");
if (!File.Exists (filename)) {
File.Create (filename);
var webClient = new WebClient ();
webClient.DownloadStringCompleted += (s, e) => {
var text = e.Result; // get the downloaded text
File.WriteAllText (filename, text);
};
var url = new Uri (/**myURL**/);
webClient.Encoding = Encoding.UTF8;
webClient.DownloadStringAsync (url);
I modified your sample slightly and the following works for me.
The StreamReader is only there just to re-read in the contents of the file to confirm that its the same contents in the file as that of the downloaded file:-
If you put a breakpoint there also you can manually inspect same contents as downloaded.
string directory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var filename = Path.Combine(directory, "samplefile.txt");
if (!File.Exists(filename))
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (s, e) =>
{
// Write contents of downloaded file to device:-
var text = e.Result; // get the downloaded text
StreamWriter sw = new StreamWriter(filename);
sw.Write(text);
sw.Flush();
sw.Close();
sw = null;
// Read in contents from device and validate same as downloaded:-
StreamReader sr = new StreamReader(filename);
string strFileContentsOnDevice = sr.ReadToEnd();
System.Diagnostics.Debug.Assert(strFileContentsOnDevice == text);
};
var url = new Uri("**url here**, UriKind.Absolute);
webClient.Encoding = Encoding.UTF8;
webClient.DownloadStringAsync(url);
}

Create doc file from template and adding data from database using open xml

I have a word template and I want to create doc file from that, also I want to replace add data in place of bookmarks present in the template.
I have been able to create a doc file, but I am not able to understand, how to add data in place of bookmarks?
My code till now:
private void CreateSampleWordDocument()
{
string sourceFile = Path.Combine(Environment.CurrentDirectory, "GeneralWelcomeLetter.dotx");
string destinationFile = Path.Combine(Environment.CurrentDirectory, "Sample.docx");
try
{
File.Copy(sourceFile, destinationFile, true);
WordprocessingDocument document = WordprocessingDocument.Open(destinationFile, true);
document.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
MainDocumentPart mainPart = document.MainDocumentPart;
DocumentSettingsPart documentSettingPart1 = mainPart.DocumentSettingsPart;
AttachedTemplate attachedTemplate1 = new AttachedTemplate() { Id = "relationId1" };
documentSettingPart1.Settings.Append(attachedTemplate1);
}
catch
{
}
}
Now to add data from database in place of bookmarks?

Streaming OpenXML result

By using OpenXML to manipulating a Word document (as a template), the server application saves the new content as a temporary file and then sends it to user to download.
The question is how to make these content ready to download without saving it on the server as a temporary file? Is it possible to save OpenXML result as a byte[] or Stream instead of saving it as a file?
Using this page:
OpenXML file download without temporary file
I changed my code to this one:
byte[] result = null;
byte[] templateBytes = System.IO.File.ReadAllBytes(wordTemplate);
using (MemoryStream templateStream = new MemoryStream())
{
templateStream.Write(templateBytes, 0, (int)templateBytes.Length);
using (WordprocessingDocument doc = WordprocessingDocument.Open(templateStream, true))
{
MainDocumentPart mainPart = doc.MainDocumentPart;
...
mainPart.Document.Save();
templateStream.Position = 0;
using (MemoryStream memoryStream = new MemoryStream())
{
templateStream.CopyTo(memoryStream);
result = memoryStream.ToArray();
}
}
}
You can create the WordprocessingDocument and then use the Save() method to save it to a Stream.
http://msdn.microsoft.com/en-us/library/cc882497
var memoryStream = new MemoryStream();
document.Clone(memoryStream);