After a long time lurking, its time to ask my first question...
I'm having trouble with an AJAX query that used to work prior to moving to AWS.
Previously, My web site was hosted on a WAMP server for testing and development and the following relevant code worked fine.
//Read XML file from disc and send file content to other functions\\
function Get_XML_File(){
var XML_File_Path = File_Path + Client_Code + '/' + ID + '/' + ID + '_Analysed_Wave.web'
var xhttps = new XMLHttpRequest();
xhttps.onreadystatechange = function() {
if (xhttps.readyState == 4 && xhttps.status == 200){
Read_Meta_Data(xhttps)
Read_Time_Status(xhttps)
Read_Wave_Height(xhttps)
;}
};
xhttps.open("GET", XML_File_Path, true);
xhttps.send();
}
//Extract Header Data from XML file\\
function Read_Meta_Data(xml) {
var xmlDoc = xml.responseXML;
// Client//
var Client_ID = xmlDoc.getElementsByTagName('Client_ID')[0].childNodes[0]
var Client_Name = xmlDoc.getElementsByTagName('Client_Name')[0].childNodes[0]
Recently, This site was moved to a Elastic Beanstalk distribution with AWS.
'www.atmocean.com.au' has been provisioned with an SSL certificate using the AWS certificate manager.
'assets.atmocean.com.au' is also covered by an SSL certificate and is mapped to a cloudfront distribution of a S3 bucket.
Within the S3 bucket are xml formatted files with a .web suffix (these are generated by proprietary software.)
When the relevent web page is viewed, the chrome console shows the following error: "Uncaught TypeError: Cannot read property 'getElementsByTagName' of null"
this error is in reference to this line:
var Client_ID = xmlDoc.getElementsByTagName('Client_ID')[0].childNodes[0]
What I can't understand is that when the 'Network' tab of the developer console is viewed, the resource is shown as correctly loaded with a status code of 200.
Further, the file content can be viewed in the 'response' tab.
Does this mean that the file has been correctly downloaded from the server to the client?
If so, why does code that formerly worked without error now fail to get the file content?
Does something other than a standard website configuration need to be provisioned through elastic beanstalk (or other means)?
Thanks in anticipation.
You receive a HTTP 200 meaning the server understand the request and can full-fill the request but then it delivers the content, when you execute Read_Meta_Data it does not mean the full content has been delivered
you could add a console.log(xml) and console.log(xmlDoc) to see the current content of your progress
what I would suggest you leverage your code to add a listener on the completion of the transfer
var xhttps = new XMLHttpRequest();
xhttps.overrideMimeType('text/xml');
xhttps.addEventListener("load", transferComplete, false);
function transferComplete(evt) {
// from here your transfer has been completed
}
note: there's also a loadend method which runs when the load has been completed (wether it has been successful or not - never used it)
Frederic,
Thanks for your response.
The clue was in the following line:
xhttp.overrideMimeType('text/xml');
because the xml files use a custom file extension it was being returned as a text string.
I've changed the function to now read as follows:
//Read XML file from disc and send file content to other functions\\
function Get_XML_File(){
var XML_File_Path = File_Path + Client_Code + '/' + ID + '/' + ID + '_Analysed_Wave.web'
var xhttp = new XMLHttpRequest();
xhttp.overrideMimeType('text/xml');
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200){
Read_Meta_Data(xhttp)
Read_Time_Status(xhttp)
Read_Wave_Height(xhttp)
;}
};
xhttp.open("GET", XML_File_Path, true);
xhttp.send();
}
xhttp.overrideMimeType('text/xml');
And with that one change, all is well with the world. (well my function at least.)
Related
My web page has a link
href="https://...myapi.../files/get-user-role-file?fileID=etc..." target="_blank"
The web api call (get-user-role-file) returns a file with a mime type and a content disposition of inline.
the code returns a HttpResponseMessage. here is some of the code
ByteArrayContent content = new ByteArrayContent(_bytes);
string dispositionType = "inline";
if (_mimetype.IndexOf("octet-stream") > 0) dispositionType = "attachment";
ContentDispositionHeaderValue contentDisposition = new ContentDispositionHeaderValue(dispositionType) {
FileName = _filename
};
MediaTypeHeaderValue contentType = new MediaTypeHeaderValue(_mimetype);
response.Content = content;
response.Content.Headers.ContentDisposition = contentDisposition;
response.Content.Headers.ContentLength = _bytes.Length;
response.Content.Headers.ContentType = contentType;
This works in chrome and safari and firefox. In Edge it works for me in VS debug or in InPrivate mode or when run locally from my machine in IIS. It also works if the file type is one that can be opened in the browser OR if I have Fiddler running. BUT it does not work, for example, with a Excel doc in Edge run normally off of the web server. It does work for everyone else I have asked so far.
I have tried updating Edge and clearing all history.
What happens is a new tab is opened with the file URL and the downloads modal opens with a file named get-user-role-file.json and a message says "Couldn't download - Something went wrong...". In the network tab of the dev tools the call returns OK 200 and no error but also no response (Failed to load response data: No resource with given identifier found).
It's a mystery.
I am able to successfully attach PDF file with ServiceNow table record using GlideSysAttachment API and attachment.write() function in script, however whenever I download and try to open same, I get the error shown in below screenshot.
Code snippet
(function execute() {
try{
var rec = new GlideRecord('incident');
var attachment = new GlideSysAttachment();
var incidentSysID = incident.number;
rec.get(incidentSysID);
var fileName = 'Test_Incident.pdf';
var contentType = 'application/pdf'; // Also tried with contentType as 'text/pdf'
var content = pdf_content;
var agr = attachment.write(rec, fileName, contentType, content);<br>
gs.info('The PDF attachment sys_id is: ' + agr);
}catch(err){
gs.log('Got Error: ' + err);
gs.info(err);
}
})()
I also tried "AttachmentCreator" with ecc_queue within script but same error occurs. Below is code for it.
(function execute()
{var attCreator = new GlideRecord('ecc_queue');
attCreator.agent = "AttachmentCreator";
attCreator.topic = "AttachmentCreator";
attCreator.name = "Test.pdf" + ":" + "text/pdf";
//Also tried, "Test.pdf:application/pdf"
attCreator.source = "incident"+":"+ incident.number;
// Record Table name and sys_id of the particular record
var content = pdf_content; // pdf_content is any string variable
var stringUtil = new GlideStringUtil();
var base64String = stringUtil.base64Encode(content);
var isValid=GlideStringUtil.isBase64(base64String);
var base64String= gs.base64Encode(content);
gs.info("Is valid base64 format in ecc_queue ? "+ isValid);
attCreator.payload = base64String; //base64 content of the file
attCreator.insert();
})()
I am able to attach and view excel and word files with similar scripts without any issues. I have checked system properties for attachments but everything looks fine. I am able to view the PDF file uploaded from UI to particular table records however not the one I attach via REST API or scripts.
I have also tried sending encoded data as bytes, base64 or simple string but nothing seems to work. I don't get any errors and attachment id is returned each time on creation of attachment.
After modifying my code slightly for above functions w.r.t scoped application instead of global; I got some information from logs when I debug:
05:38:38.411 Security restricted: File type is not allowed or does not match the content for file Test.pdf
05:38:38.410 Security restricted: MIME type mismatch for file: Test.pdf. Expected type:application/pdf, Actual type: text/plain
05:38:38.394 App:XYZ App x_272539_xyz_ap: Is valid base64 format in ecc_queue ? true
First a comment: This line in your code is accidentally working -- make sure you understand that a task number is not the object sys_id
var incidentSysID = incident.number; // should be incident.sys_id
Next, it's unclear where the PDF content is coming from. IF your complete code is listed, I would expect the errors given as you have noted that pdf_content is "any string variable."
ServiceNow does have a the capability to create a PDF from an HTML argument.
Generating a PDF from HTML
Here's a helpful blog post for getting a PDF (Platform generated) of an existing record:
Love PDF? PDF loves you too
I try to find solution to my problems but didn't find any where,hope that someone here can save me.
I write add-in in JavaScript on VS2015 that encrypte and decrypte body messages.
1. The first problem is with images that the receiver can't see .
(Talk about images that copy into the body by "insert picture inline" button)
In Compose mode we encrypte the message and then when we decrypte it's works good because the compose mode is a client side and he his recognize the local images .
In read mode when user want to decrypte the message and to see the images he couldn't see because the encrypte prevent outlook to convert the local image to data on the server .
In my code I take the body message like this ( compose mode )
item.body.getAsync(
item.body.getAsync(
"html",
{ asyncContext: "This is passed to the callback" },
function callback(resultbody) {
......Here we send the body for ENCRYPT.
}))
then , the user send the encrypte message by clicking 'send' regular.
In the read mode I just print it to my html to check if the decrypte is good :
(JSON.parse(xhr.responseText).Data.Content));
and then i get icon of picture ,but not success to show the real pic .
The src of the icon is going for place that not access for him ..
<img src="https://attachment.outlook.office.net/owa/*****/service.svc/s/GetFileAttachment?id=AAMkADUwMDE0YWM1LTYwODctNG ......
How can i take this tag of image and do something that the receiver can see the image ? I don't want that user will be need to upload image to body from my my add-in instead of the original outlook. I try to convert the image to base-64 string, but with what I have in the tag it not enough ,just with original picture and also it success to show in html but not in the body of message with SetAsync function..
2. The second problem is with attachments .
I upload files with dropzone plug-in (because outlook don't give access to take attachment and change him). So, after I upload files and encrypte him I make some new file with the response from server with File API of JS :
ar f = new File([""], "filename.txt", {type: "text/plain", lastModified: date}) . .. .
than I want to attach the file to mail, so the only method that do this is:
addFileAttachmentAsync(uri, attachmentName, optionsopt, callback opt)
then,I need to create a url for file for this method so I use this method:
var objectURL = URL.createObjectURL(f);
But now when I use the method addFileAttachmentAsync with objectURL it's write that there is a problem and its can't attach it , I think that the URL is incorrect .
Thanks all!!
For everyone who look any solution to this problems..
**In outlook web this solutions works good but in Outlook Desktop there is a problem of synchronize with server so there is a delay with saveAsync function without any solution to this right now , so it's work but need to wait a little bit.You could read more about it here.
First Question:
There is a problem in outlook add-in with when using getAsync and then setAsync functions . The problem occurs when there is some image inside the body . It's happen because when you take the body in Html format and then return the body with some different the image still not 'upload' and the src is being wrong .
I success to workaround this problem using Outlook rest API.
So the workaround is going like this:
Get the body message in type of Html by getAsync method. create div
element and set the return body message inside the div.
To get message id, you need to save your message as a draft with saveAsync function.
To make request to Outlook rest
API you need to get access token , so call to getCallbackTokenAsync function and save the access
token.
Make Http Request to outlook rest API to get all attachment exist in
the message.
Find the right ID of your image and replace the image src to the
base-64 of the image that you get from your request to outlook rest
API.
Finally , you could set your new body with SetAsync function .
Code:
item.body.getAsync(
Office.CoercionType.Html,
{ asyncContext: "This is passed to the callback" },
function callback(resultbody) {
var bodyDiv = document.createElement('div');
bodyDiv.innerHTML = content;
Office.context.mailbox.item.saveAsync(
function callback(result) {
var myNewItemSaved = result.value;
Office.context.mailbox.getCallbackTokenAsync({ isRest: true },
function (result) {
if (result.status === "succeeded") {
var accessToken = result.value;
var itemId = "";
if (Office.context.mailbox.diagnostics.hostName === 'OutlookIOS')
itemId = Office.context.mailbox.item.itemId;
else
itemId = Office.context.mailbox.convertToRestId(myNewItemSaved,
Office.MailboxEnums.RestVersion.v2_0);
var xhr3 = new XMLHttpRequest();
xhr3.open("GET", "https://outlook.office.com/api/v2.0/me/messages/" + itemId + "/attachments", true);
xhr3.setRequestHeader("Content-type", "application/json");
xhr3.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr3.setRequestHeader("Authorization", "Bearer " + accessToken);
xhr3.send();
xhr3.onreadystatechange = function () {
if (xhr3.readyState == 4) {
if (xhr3.status == 200) {
var allImages = JSON.parse(xhr3.response).value;
var isDesktop = false;
var imgSrcId = bodyDiv.getElementsByTagName('img')[0].getAttribute("src");
if (imgSrcId.indexOf("cid") != -1) //Outlook Desktop
isDesktop = true;
for (var i = 0; i < allImages.length; i++) {
if (bodyDiv.getElementsByTagName('img')[i].getAttribute("src").indexOf("base64")!=-1)
continue;
if (isDesktop)
imgSrcId = bodyDiv.getElementsByTagName('img')[i].getAttribute("src");
else
imgSrcId = bodyDiv.getElementsByTagName('img'[i].getAttribute("originalsrc");
imgSrcId = imgSrcId.substr(4, imgSrcId.length);
var wantedImg;
for (var j = 0; j < allImages.length; j++) {
if ((allImages[j].ContentId).localeCompare(imgSrcId) != -1) {
wantedImg = allImages[j]; break;}
}
bodyDiv.getElementsByTagName('img')[i].src = 'data:' + wantedImg.ContentType + ';base64,' + wantedImg.ContentBytes;
}
}
setAsync......
}
}}}})})};
Second question
The problem with addFileAttachmentAsync that this is work only with files that is on external server, and it's not add a blob , local files.
So also here the solution is with Outlook rest API . The solution will attach our file to the message but we can't see this-no preview of the attachment in message , but when we send it this will attach to message , and we could see in our message that the attachment is there.
The solution is really similar to the one of the image in body - Save the message as a draft , get access token and this time the Http Request will be 'POST' request to our message id to attach our file to the current message.
Code to the request to add attachment to message ( all the way until here is the same like question 1):
var attachment ={
"#odata.type": "#Microsoft.OutlookServices.FileAttachment",
"Name": "smile.png",
"ContentBytes": "AAACFAMxLjAeKUDndY7EKF4P7QiWE7HgHLa7UiropGUTiDp5V07M0c5jaaTteauhzs0hOU+EOmVT0Lb6eSQ2MzgkCre/zCV9+kIB9PjWnOzoufau67J9PQdXapsOQSMcpt9X2QpcIjnl7H3sLu9iu2rqcvSjwhDnK6JygtghUB405EZHZ9LQcfJ1ZTYHylke2T9zbViq2BPqU/8IHZWsb/KQ/qzV4Jwv3NHnI583JvOuAtETJngh964edC4cU2IY6FkIWprksRw7d4fEQ/+3KbEyW0trIZm59jpTSV01/PhOI0RDKj1xI1Vr+lgMRZpOrYDfChWWWbByNzSXbIsTjHMU6GmQ5Cb09H3kv/2koFa5Pj2z8i+NGywYKw8ZSu3NVblM9I0EkQVLrxkM8gqyrDEtAobxPRxEzGTEXdnjws5UIiiGFBq3khuxejFGCNvUbmPM9guVZO0ccDe1FICTFHkrPlLZW/TvJYMou0HBrvH7s4taBHyZw5x03dhps+WG19D5na44vaVX2Vni6ZrrxfqFo7JTUpCJxCcPyoG7/nEWtJ/V/J+oXdypeapN9Agl6Q81WvCbzuyZgbLTfj6NXWDoliie069Hvk/k2lP+HyO7Iu5ffeRX2WWguwdfGXiNbqInrxn18tX+N7/KqWbRJv96tmijdCmCvsF9Lpr9k7QFKB93wuHfTuE6Qi2IVNBfzNBaz1iJYjY="
}
var xhr4 = new XMLHttpRequest();
xhr4.open("POST", "https://outlook.office.com/api/v2.0/me/messages/" + itemId + "/attachments", true);
xhr4.setRequestHeader("Content-type", "application/json");
xhr4.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr4.setRequestHeader("Authorization", "Bearer " + accessToken);
xhr4.send(JSON.stringify(attachment));
xhr4.onreadystatechange = function () {
if (xhr4.readyState == 4) {
if (xhr4.status == 200)
console.log("ok");
else
console.log(xhr4.response);
}};
Hope it's will help someone , good luck !
I am working on New web application which is Using Web API as Business Layer and Knock out Js as client side frame work to binding. I have a requirement like Pass the certain search criteria to Web API Controller and get the Data from DB and Create and Send the Excel/MS-Word file on the fly as a downloadable content.
I am new to both the Web API and Knock out, I am searching on the Net and get partial solution and I am looking here to get more optimal solution for this use case.
Below is my code:
Client:
function GetExcelFile() {
var $downloadForm = $("<form method='POST'>")
.attr("action", baseUrl + "api/FileHandler/GetExcelFileTest")
.attr("target", "_blank")
$("body").append($downloadForm);
$downloadForm.submit();
$downloadForm.remove();
}
On Button Click having this code snippet to create a form on the fly and Get response from Web API.
Web API Code:
[HttpPost]
public HttpResponseMessage GetExcelFileTest()
{
var response = new HttpResponseMessage();
//Create the file in Web App Physical Folder
string fileName = Guid.NewGuid().ToString() + ".xls";
string filePath = HttpContext.Current.Server.MapPath(String.Format("~/FileDownload/{0}", fileName));
StringBuilder fileContent = new StringBuilder();
//Get Data here
DataTable dt = GetData();
if (dt != null)
{
string str = string.Empty;
foreach (DataColumn dtcol in dt.Columns)
{
fileContent.Append(str + dtcol.ColumnName);
str = "\t";
}
fileContent.Append("\n");
foreach (DataRow dr in dt.Rows)
{
str = "";
for (int j = 0; j < dt.Columns.Count; j++)
{
fileContent.Append(str + Convert.ToString(dr[j]));
str = "\t";
}
fileContent.Append("\n");
}
}
// write the data into Excel file
using (StreamWriter sw = new StreamWriter(fileName.ToString(), false))
{
sw.Write(fileContent.ToString());
}
IFileProvider FileProvider = new FileProvider();
//Get the File Stream
FileStream fileStream = FileProvider.Open(filePath);
//Set response
response.Content = new StreamContent(fileStream);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = fileName;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/ms-excel");
response.Content.Headers.ContentLength = fileStream.Length;
//Delete the file
//if(File.Exists(filePath))
//{
// File.Delete(filePath);
//}
return response;
}
Using this code I am able to download an Excel File. Still I have some more open questions to make this code optimal.
Q1) I need to Pass view model(Search Criteria) to API Controller Using the dynamically create form ? (OR) Any better ways to get Excel file from Web API.
Q2) I am sure it's not a good way to create Excel file in Physical folder and Get FileStream and send as a respone. How to do on the fly ? OR any other optimal ways.
Please suggest me to do better ways.. Thanks
Q1) You can quite easily pass the view-model, but it's also similarly easy to pull that information from the posted form.
Passing the view-model
If you want to pass the view-model to a WebAPI method then remember that said method must take as a parameter an object with the same properties. So if the object that you wish to post back always has the same properties then it's trivial to build a server-side class with the same properties and receive an instance of that class.
To post back this client-side object you can do something like this (uses jQuery, which I see you're already using):
$.ajax({
contentType: "application/json",
data: my-view-model.toJSON(),
type: "POST",
url: baseUrl + "api/FileHandler/GetExcelFileTest" });
I haven't attached any success or error handlers here because the JavaScript isn't concerned with the return, but you might wish to add some handlers in case an exception is thrown in your WebAPI method. I recommend doing that by adding the following to the above $.ajax() call:
statusCode: {
500: function(jqXhr, textStatus, errorThrown) {
},
[other HTTP error codes]
}
[Read the documentation for the $.ajax() call here.]
One additional tip here: when you call my-view-model.toJSON() (or self.toJSON(), if called from within your view-model) Knockout will first of all determine if your view-model contains a toJSON() method. If so, it will use this method; if not then it will call the browser's implementation of this function. However, the browser's implementation of this function will serialise everything, which can be particularly length if you have, for example, long select lists in your view-model. Therefore, if you wish only to send back a subset of the view-model's properties then define your own toJSON function on your view-model like so:
var toJSON = function() {
return {
Property1: ...,
Property2: ...
};
}
[Read more about converting a view-model to JSON here.]
Posting the form as-is
If you don't wish to expend the effort to do the view-model wiring then you can just post the form exactly like you have in your question. You can then retrieve the values from the form by using
Request.Form["my-field"];
Q2)
You're probably right in pointing out that it's not wise to create the Excel file in the physical folder. However, as far as I'm aware (interested if someone says otherwise) you'll have to use a 3rd-party library for this. Microsoft do offer an Office automation library but I have a suspicion that you also need Office to be installed at the same location.
Creating Excel spreadsheets dynamically is something I've done several times but for the actual creation I use Aspose.Cells, which requires a license. Although I do create a physical version and then delete it, I believe Aspose.Cells may allow you to create it as a stream. But take a look around, there are certainly other libraries which offer Excel automation.
Returning the File from the Server
Calling $.ajax({...}) alone won't allow you to present the user with a "Save as..." dialog. What I do in this situation - and this won't work if you wish to store the generated file only in memory (FileStream, for example) and not on the file system - is to respond to the $.ajax({...}) call with a filename for the generated file.
The next step is to direct the user towards that filename.
So I have something like this in my JavaScript:
$.ajax({
dataType: "json",
type: "GET", // you'll probably want POST in your case
url: ...,
success: function(response) {
if (response && response.Uri && response.Uri.length) {
window.location.href = [root URL] + response.Uri;
}
}
});
But don't be alarmed by this redirect. That window.location.href points directly to a folder on the server, no controller needed. Because the browser then receives a file it presents the "Save as..." dialog while remaining on the same webpage.
I am trying to use the multiple upload attribute of HTML5 to upload files.
I know it wouldn't work with IE and fall back to single file upload.
also I found some invalid html tag like min max allows opera to do the same.
I am trying to do the following:
The browse button be capable of selecting multiple files.
But the ajax should send files one by one.
My scenario is something like this:
the user selects 5 files and starts the upload . Now the ajax should firstfile send the first file, then second, and so on.
The server side script does something with the file and returns some data.
now as soon as one file upload is completed it must render that part of the result.
So as the user selects images and starts uploading the results come out as soon as each file is uploaded (and not after all the files are uploaded).
I tried something like this :
function handleFiles(files)
{ alert(files.length); //properly returns the number of files selected
for (var i = 0; i < files.length; i++) {
new FileUpload(files[i])
}
}
function FileUpload(file) {
var reader = new FileReader();
var xhr = new XMLHttpRequest();
this.xhr = xhr;
xhr.open("POST", "portfolio/add_media");
reader.onload = function(evt) {
xhr.sendAsBinary(evt.target.result);
};
reader.readAsBinaryString(file);
}
after reading the tutorial at mozilla but I end up with missing params.
. so can some one suggest me a clean solution to this
Some more details :
When I pass a single file ( with no multiple attribute ) my server recieves :
"image"=>[# < ActionDispatch::Http::UploadedFile:0x10d55be8
#tempfile=#< File:C:/Users/Gaurav/AppData/Local/Temp/RackMultipart20110701-1916-2ly4k2-0>,
#headers="Content-Disposition:
form-data; name=\"picture[image][]\";
filename=\"Desert.jpg\"\r\nContent-Type:
image/jpeg\r\n",
#content_type="image/jpeg",
#original_filename="Desert.jpg">]}}
But when I use multiple attribute and send using xhr I am able to get only one file param. How do I get the rest of the params ? esp the action dispatch thingy
You are simply sending the file data to the server, without encoding it in any way. For the server to know how to process it you need to encode your data properly (multipart/form-data encoding). Easiest way is using a FormData object: https://developer.mozilla.org/En/Using_XMLHttpRequest#Sending_files_using_a_FormData_object. Only that instead of data.append("CustomField", "This is some extra data") you would write data.append("file1", event.target.result).