1

What I'm Trying To Do

I inherited an angular project and am new to the framework. I am trying to open a macro enabled word document using Spire.Doc (free version) and edit the file, then send the edited file to the client. I am calling a. Net Core 3 Web API from an angular component through a report service. I need to send an ID to the API, open and edit the word file, then send it back to the client.

What I'm Getting

I have had various results from all of the changes I have tried. With the code below, I am able to download a word document, but the one page file is now 54 pages of random characters (wrong encoding?).

My Code

client.component.ts

public createForm() {
this.reportService.GetForm(this.clientId);
}

reports.service.ts

GetForm(clientId: string) {
    let params = new HttpParams()
        .set('clientId', clientId);
    this.http.get(environment.apiUrl + '/Reports/GenerateForm', { params: params, responseType: 'blob' })
        .subscribe(response => {
            let blob = new Blob([response], { type: 'application/msword' });
            const fileName = "Test Word Document.doc";
            saveAs(blob, fileName);
        });
}

ReportsController.cs

[HttpGet("GenerateReimbursementForm")]
public async Task<ActionResult<HttpResponseMessage>> GenerateForm(int clientId)
{
    var loggedInUser = this.GetLoggedInUser(this._context);
    if (!loggedInUser.HasSystemAccess)
        return Forbid();

    // get document
    string filename = "Test Word Document.doc";
    string path = Path.Combine(
        Directory.GetCurrentDirectory(), 
        "documents", 
        filename);

    Spire.Doc.Document doc = new Spire.Doc.Document(path);

    // do stuff to document
    Spire.Doc.Documents.Paragraph p = doc.CreateParagraph();
    p.Text = "testing...";

    //get stream from document in memory
    MemoryStream ms = new MemoryStream();
    doc.SaveToFile(ms, Spire.Doc.FileFormat.Doc);
    ms.Position = 0;

    // send to browser in http response message
    var result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new StreamContent(ms);
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    result.Content.Headers.ContentDisposition.FileName = filename;
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentLength = ms.Length;

    return result;
}

Things I've Tried

  • Different word document - same result
  • Different word types (docx, docm) - word says it is a corrupted file
  • Using different type values (all with the same result):
    • blob
    • application/vnd.openxmlformats-officedocument.wordprocessingml.document
    • arraybuffer
  • Adding charset:utf-8; to type string
  • Removing the Spire.Doc code - same result
  • Return a byte[] from controller instead of HttpResponseMessage using memoryStream.ToArray() - same result
  • Create and save the blob in the component code rather than the service - same result
  • make the link button redirect to the controller path and skip the .ts files - unable to get this method to work due to path issues

Any help is appreciated!

Edit

When making a GET request through Postman I receive the following:

{"version":{"major":"1","minor":"1","build":"-1","revision":"-1","majorRevision":-1,"minorRevision":-1},"content":{"headers":[{"Key":"Content-Disposition","Value":["attachment; filename=\"Test Word Document.doc\""]},{"Key":"Content-Type","Value":["application/octet-stream"]},{"Key":"Content-Length","Value":["16384"]}]},"statusCode":200,"reasonPhrase":"OK","headers":[],"trailingHeaders":[],"requestMessage":null,"isSuccessStatusCode":true}
Thomas G
  • 63
  • 5
  • 1
    What happens if you call the API from a HTTP client directly (e.g. Postman) and store the response byte stream and open it with Word? If the result is correct, the issue is with the front-end, if not, the issue is with the back-end. Either way, you will be able to ask a shorter, more specific question that will probably attract answers faster :) – Zdeněk Jelínek Sep 24 '20 at 18:44
  • I updated my question with the result of the get request. I am not familiar with APIs or Postman so let me know if there is something else I can get from it. – Thomas G Sep 24 '20 at 19:36
  • Yes, what you have provided is some JSON-serialized object including headers. Please check out e.g. [How to download excel (.xls) file from API in postman?](https://stackoverflow.com/questions/38975718/how-to-download-excel-xls-file-from-api-in-postman), save the response as a docx file and try to open it with Word and see if it's correct as per my previous comment. Alternatively, in addition to passing it as a response, you might also store a duplicate of the returned memory stream/byte array to a new file, if your environment permits it. The result should be the same. – Zdeněk Jelínek Sep 24 '20 at 20:15
  • I did the "send and download" in Postman and it placed the JSON object into the word document instead of the original content. – Thomas G Sep 24 '20 at 20:39
  • That is really strange. What do you get when you `ToArray()` the response memory stream on .NET side? If it is a string, could you `Encoding.GetString()` it? Alternatively, could you save it to a file? – Zdeněk Jelínek Sep 24 '20 at 20:46
  • The `ToArray()` gives me a `byte[]`. I tried multiple ways of decoding the array to a string but got symbols in the document instead of the normal text. I tried saving it as a temp file and used `ControllerBase.File()` to return a `FileStreamResult`. This returned the word document like I wanted. Thanks for your help @ZdeněkJelínek! – Thomas G Sep 25 '20 at 15:35
  • This is very strange behavior. I'm afraid your solution will result in performance and other issues (e.g. what if the controller is called multiple times concurrently?) but at least you got it working and also have a better idea of where the issue might be for possible future investigation, i.e. not in the Angular code. – Zdeněk Jelínek Sep 25 '20 at 16:45

0 Answers0