I am building an add-in for Word, with the goal of being able to save the open Word document to our MVC web application. I have followed this guide and am sending the slices like this:
function sendSlice(slice, state) {
var data = slice.data;
if (data) {
var fileData = myEncodeBase64(data);
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState == 4) {
updateStatus("Sent " + slice.size + " bytes.");
state.counter++;
if (state.counter < state.sliceCount) {
getSlice(state);
}
else {
closeFile(state);
}
}
}
request.open("POST", "http://localhost:44379/api/officeupload/1");
request.setRequestHeader("Slice-Number", slice.index);
request.setRequestHeader("Total-Slices", state.sliceCount);
request.setRequestHeader("FileId", "abc29572-8eca-473d-80de-8b87d64e06a0");
request.setRequestHeader("FileName", "file.docx");
request.send(fileData);
}
}
And then receiving the slices like this:
public void Post()
{
if (Files == null) Files = new Dictionary<Guid, Dictionary<int, byte[]>>();
var slice = int.Parse(Request.Headers.GetValues("Slice-Number").First());
var numSlices = int.Parse(Request.Headers.GetValues("Total-Slices").First());
var filename = Request.Headers.GetValues("FileName").First();
var fileId = Guid.Parse(Request.Headers.GetValues("FileId").First());
var content = Request.Content.ReadAsStringAsync().Result;
if (!Files.ContainsKey(fileId)) Files[fileId] = new Dictionary<int, byte[]>();
Files[fileId][slice] = Convert.FromBase64String(content);
if (Files[fileId].Keys.Count == numSlices)
{
byte[] array = Combine(Files[fileId].OrderBy(x => x.Key).Select(x => x.Value).ToArray());
System.IO.FileStream writeFileStream = new System.IO.FileStream("c:\\temp\\test.docx", System.IO.FileMode.Create, System.IO.FileAccess.Write);
writeFileStream.Write(array, 0, array.Length);
writeFileStream.Close();
Files.Remove(fileId);
}
}
The problem is that the file that is produced by the controller is unreadable in Word. I have tested with a word document with "Test123" as the entire contents of the document, and when the file is saved through word it is 13kb, but when sent to the web app and saved from there the file is 41kb.
My assumption is that the I am missing something either with the encoding or decoding, since I am only sending a single slice so there shouldn't be an issue with recombining them.
There's an Excel snippet in Script Lab that produces the base64 encoded file which you can paste into an online decoder like www.base64decode.org. The APIs are the same as in Word. This can help you isolate the encoding code. After you install Script Lab, open the Samples tab, scroll to the Document section. It's the Get file (using slicing) snippet.
Related
I have a nodejs server that sends a GET request with axios to a c# endpoint with json as a parameter. My c# api uses Newtonsoft.Json to deserialize the json, then it reads a word file into memory, and inserts data. The final step I need is for this api to respond by sending the modified document back to the nodejs server. Currently, the c# endpoint is called, and a response is sent back. Upon writing the word document using the archiver library and opening it, a dialogue box appears, saying "Word found unreadable content in export0.docx. Do you want to recover the contents of this document? If you trust the source of this document, click Yes"
async exportToDotnet() {
return await axios.get(`https://localhost:8082/test/${JSON.stringify(this)}`, { responseType: 'arrayBuffer' }).catch(err => {
console.log(`ERR `, err);
}).then((axiosResponse) => {
const data = axiosResponse.data;
console.log(`DATA `, data);
console.log(`DATA LENGTH '${data.length}'`);
return data;
});
}
async function writeZipFile(resultFromExportToDotnet) {
const output = createWriteStream('exported.zip');
output.on("close", () => {
console.log("success????");
});
const archive = archiver('zip');
archive.on('error', (err) => {
console.log('error in archive ', err);
});
archive.append(form, { name: `export0.docx` });
archive.pipe(output);
await archive.finalize();
}
[HttpGet("test/{json}")]
public byte[] ExportDocumentBuffer(string json)
{
Console.WriteLine("Called");
//Converts the json passed in to a FormInstance Object
FormInstance form = JsonConvert.DeserializeObject<FormInstance>(json);
//Read the dotx into memory so we can use it. Would it be better to just use System.IO.File.ReadAllBytes()?
MemoryStream dotxBytes = ReadAllBytesToMemoryStream("test.dotx");
//Read the file bytes into a WordProcessingDocument that we can edit
WordprocessingDocument template = WordprocessingDocument.Open(dotxBytes, true);
template.ChangeDocumentType(WordprocessingDocumentType.Document);
template = ParseFormAndInsertValues(form, template);
byte[] output = dotxBytes.ToArray();
Console.WriteLine($"BYTES '{output.Length}'");
return output;
}
///<summary>Reads all Bytes of the provided file into memory</summary>
///<param name="path">The path to the file</param>
///<returns>A MemoryStream of the file data</returns>
public static MemoryStream ReadAllBytesToMemoryStream(string path)
{
byte[] buffer = System.IO.File.ReadAllBytes(path);
MemoryStream destStream = new MemoryStream(buffer.Length);
destStream.Write(buffer, 0, buffer.Length);
destStream.Seek(0, SeekOrigin.Begin);
return destStream;
}
Things I've tried
Changing the axios responsetype to 'stream', converting the response to a buffer with a function, and writing it to a file
function stream2buffer(stream) {
return new Promise((resolve, reject) => {
const _buf = [];
stream.on("data", (chunk) => _buf.push(chunk));
stream.on("end", () => resolve(Buffer.concat(_buf)));
stream.on("error", (err) => reject(err));
});
}
Changing my c# method to return a HttpResponseMessage
HttpResponseMessage result = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new ByteArrayContent(dotxBytes.ToArray())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "exampleName.docx"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Logging the length of byte[] and logging data.length produce 2 different numbers (52107 and 69476, respectively). Is this just a serialization issue? Obviously I'm missing something. Any help would be much appreciated!
Turns out to have been a few things: I used template = WordProcessingDocument.Open(), but never called template.Save() or template.Close() and as such, my changes were never written, and the file was still open. Once I got my byte array output, I used Convert.ToBase64String(output) and returned the string. On the NodeJs side, I changed the responsetype to 'text', and returned Buffer.from(axiosResponse.data, 'base64'); and wrote the file that way.
I'm working on Pdf to text file conversion using google cloud vision API.
I got an initial code help through there side, image to text conversion working fine with JSON key which I got through registration and activation,
here is a code which I got for pdf to text conversion
private static object DetectDocument(string gcsSourceUri,
string gcsDestinationBucketName, string gcsDestinationPrefixName)
{
var client = ImageAnnotatorClient.Create();
var asyncRequest = new AsyncAnnotateFileRequest
{
InputConfig = new InputConfig
{
GcsSource = new GcsSource
{
Uri = gcsSourceUri
},
// Supported mime_types are: 'application/pdf' and 'image/tiff'
MimeType = "application/pdf"
},
OutputConfig = new OutputConfig
{
// How many pages should be grouped into each json output file.
BatchSize = 2,
GcsDestination = new GcsDestination
{
Uri = $"gs://{gcsDestinationBucketName}/{gcsDestinationPrefixName}"
}
}
};
asyncRequest.Features.Add(new Feature
{
Type = Feature.Types.Type.DocumentTextDetection
});
List<AsyncAnnotateFileRequest> requests =
new List<AsyncAnnotateFileRequest>();
requests.Add(asyncRequest);
var operation = client.AsyncBatchAnnotateFiles(requests);
Console.WriteLine("Waiting for the operation to finish");
operation.PollUntilCompleted();
// Once the rquest has completed and the output has been
// written to GCS, we can list all the output files.
var storageClient = StorageClient.Create();
// List objects with the given prefix.
var blobList = storageClient.ListObjects(gcsDestinationBucketName,
gcsDestinationPrefixName);
Console.WriteLine("Output files:");
foreach (var blob in blobList)
{
Console.WriteLine(blob.Name);
}
// Process the first output file from GCS.
// Select the first JSON file from the objects in the list.
var output = blobList.Where(x => x.Name.Contains(".json")).First();
var jsonString = "";
using (var stream = new MemoryStream())
{
storageClient.DownloadObject(output, stream);
jsonString = System.Text.Encoding.UTF8.GetString(stream.ToArray());
}
var response = JsonParser.Default
.Parse<AnnotateFileResponse>(jsonString);
// The actual response for the first page of the input file.
var firstPageResponses = response.Responses[0];
var annotation = firstPageResponses.FullTextAnnotation;
// Here we print the full text from the first page.
// The response contains more information:
// annotation/pages/blocks/paragraphs/words/symbols
// including confidence scores and bounding boxes
Console.WriteLine($"Full text: \n {annotation.Text}");
return 0;
}
this function required 3 parameters
string gcsSourceUri,
string gcsDestinationBucketName,
string gcsDestinationPrefixName
I don't understand which value should I set for those 3 params.
I never worked on third party API before so it's a little bit confusing for me
Suppose you own a GCS bucket named 'giri_bucket' and you put a pdf at the root of the bucket 'test.pdf'. If you wanted to write the results of the operation to the same bucket you could set the arguments to be
gcsSourceUri: 'gs://giri_bucket/test.pdf'
gcsDestinationBucketName: 'giri_bucket'
gcsDestinationPrefixName: 'async_test'
When the operation completes, there will be 1 or more output files in your GCS bucket at giri_bucket/async_test.
If you want, you could even write your output to a different bucket. You just need to make sure your gcsDestinationBucketName + gcsDestinationPrefixName is unique.
You can read more about the request format in the docs: AsyncAnnotateFileRequest
I am using JSReport in order to generate reports in .netCore2; In the below method the view is returned to the user and report is saved in specified directory;
public IActionResult ImageDownload()
{
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf)
.Configure((r) => r.Template.Phantom = new Phantom
{
Format = PhantomFormat.A4,
Orientation = PhantomOrientation.Portrait
}).OnAfterRender( (r) =>
{
var streamIo = r.Content; // streamIo is of type System.IO
streamIo.CopyTo(System.IO.File.OpenWrite("C:GeneratedReports\\myReport.pdf"));
streamIo.Seek(0, SeekOrigin.Begin);
}
);
var dp = new Classes.DataProvider();
var lstnames = dp.GetRegisteredNames();
var lst = lstnames.ToArray<string>();
return View("Users", lst);
}
Once the view is returned a new browser opens displaying the pdf report. Also the same pdf will be saved in the webserver in the given directory; The problem is that the created report in the directory seems to be locked, I cannot copy it, open it... unless I close the .net solution. Any explanation of what's happening here?
You FileStream isn't closed when OnAfterRender has completed meaning no other app can open/access it. Try changing the code to put a using block around the File.OpenWrite call to contain a using statement for the FileStream e.g.
public IActionResult ImageDownload()
{
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf)
.Configure((r) => r.Template.Phantom = new Phantom
{
Format = PhantomFormat.A4,
Orientation = PhantomOrientation.Portrait
}).OnAfterRender( (r) =>
{
var streamIo = r.Content; // streamIo is of type System.IO
using(var fs = System.IO.File.OpenWrite("C:GeneratedReports\\myReport.pdf"))
{
streamIo.CopyTo(fs);
}
streamIo.Seek(0, SeekOrigin.Begin);
}
);
var dp = new Classes.DataProvider();
var lstnames = dp.GetRegisteredNames();
var lst = lstnames.ToArray<string>();
return View("Users", lst);
}
I am trying to browse and upload a file from client to server using Angular Js and WEB API.I used Input file type for user to select file and post the file to WEB API. In web API, I am getting following error "This method or property is not supported after HttpRequest.GetBufferlessInputStream has been invoked."
I am using the following code:-
public IHttpActionResult UploadForm()
{
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = System.Web.HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = System.Web.HttpContext.Current.Server.MapPath("~/UploadFile/" + postedFile.FileName);
postedFile.SaveAs(filePath);
}
}
return Json("Document Saved");
}
I get this error when i tried to get files from HTTP request... should I update anything in web config??
Please help me to resolve this issue..
try this it work fine for me.
//get the root folder where file will be store
string root = HttpContext.Current.Server.MapPath("~/UploadFile");
// Read the form data.
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
if (provider.FileData.Count > 0 && provider.FileData[0] != null)
{
MultipartFileData file = provider.FileData[0];
//clean the file name
var fileWithoutQuote = file.Headers.ContentDisposition.FileName.Substring(1, file.Headers.ContentDisposition.FileName.Length - 2);
//get current file directory on the server
var directory = Path.GetDirectoryName(file.LocalFileName);
if (directory != null)
{
//generate new random file name (not mandatory)
var randomFileName = Path.Combine(directory, Path.GetRandomFileName());
var fileExtension = Path.GetExtension(fileWithoutQuote);
var newfilename = Path.ChangeExtension(randomFileName, fileExtension);
//Move file to rename existing upload file name with new random filr name
File.Move(file.LocalFileName, newfilename);
}
}
I also had the same problem. And the solution by #Jean did not work for me.
I need to upload some CSV file and had to use it in the controller.
In Javascript, I used Fetch API to upload the csv file.
But, in the controller, I used this code:
[HttpPost]
[CatchException]
public bool ImportBundlesFromCsv()
{
var a = Request.Content.ReadAsByteArrayAsync();
//convert to Stream if needed
Stream stream = new MemoryStream(a.Result); // a.Result is byte[]
// convert to String if needed
string result = System.Text.Encoding.UTF8.GetString(a.Result);
// your code
return true;
}
This worked for me. Hope this helps!
I want to create a WCF service (working like windows service). This service will read a PDF file from a specific path, extract pages, create a new PDF file and return it to the caller.
How can I do this ? I use QuickPDF to process on PDF files, I can extract and create new PDF file. How can use this in a WCF service ?
Waiting your helps...
This is only sample code :
public Stream ExtractPdf(string PathOfOriginalPdfFile, int StartPage,int PageCount)
{
PDFLibrary qp = new PDFLibrary();
Stream Stream_ = null;
if (qp.UnlockKey(".................") == 0)
{
string fileName = #"..\..\Test Files\sample1.pdf";
string OutputFile = #"..\..\Test Files\sample1_extracted.pdf";
if (qp.Unlocked() == 1)
{
int docID = qp.LoadFromFile(fileName, "");
int extractPageSuccess = qp.ExtractPages(StartPage, PageCount);
if (extractPageSuccess == 0)
{
// error
}
else
{
qp.SaveToFile(OutputFile);
}
}
}
//
// Codes here
//
return Stream_;
}
I edited it :
public byte[] ExtractPdf(string PathOfOriginalPdfFile, int StartPage,int PageCount)
{
QuickPDFDLL0815.PDFLibrary qp = new QuickPDFDLL0815.PDFLibrary(#"C:\Program Files (x86)\Quick PDF Library\DLL\QuickPDFDLL0815.dll");
string fileName = #"..\..\Test Files\sample1.pdf";
byte[] binFile = null;
if (qp.UnlockKey("...................") == 0)
{
if (qp.Unlocked() == 1)
{
int docID = qp.LoadFromFile(fileName, "");
int extractPageSuccess = qp.ExtractPages(StartPage, PageCount);
if (extractPageSuccess == 0)
{
// error
}
else
{
binFile = qp.SaveToString();
}
}
}
return binFile;
}
You could send the file as a Stream, see How to: Enable Streaming, then on the client save off the file and have the shell execute it. The MSDN article includes a sample GetStream method as well as a whole section on Writing a custom stream.
If you would like fuller sample code the forum post Streamed file transfer using WCF starts with some, however, note that the author posted it there because they were encountering issues running it.
As to byte[] or stream see Uploading Image Blobs–Stream vs Byte Array and Stream vs Raw Bytes. The second states
Streams will perform better for large files since not all of it needs to be read into memory at one time