I have a text file and I’m doing some changes in it before the user downloads. All changes are made with Javascript/Typescript and don’t generate any errors. The problem I’m facing is that, when the user download the file, it always comes incomplete after a specific and unrelated word. If I console.log
before the actual download, I can see the file perfectly fine. The source of the problem seems to be an added reference to the file, because if I remove this ‘Add references’ part, the file is download as expected. Sadly I cannot remove this part.
This function was made for when the user is navigating through the browser:
myDownloadFunction(file: Features[]) {
...
// Feature is OpenLayer's Feature
// https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html
// Declare variables and minor changes
let final_output:string = kml_format.writeFeatures(file);
...
// Add references
for (let feature of this.featuresToExport) {
let idToExport = feature.id_;
let featureColor:string = feature.values_.color;
let featureHexColor = this.getColorByName(featureColor);
let colorElement = '<Style id="app_style_'+idToExport+'"><IconStyle><Icon><href>https://earth.google.com/earth/rpc/cc/icon?color='+featureHexColor+'&id=2000&scale=4</href></Icon></IconStyle></Style>';
// Add style element
let indexOfDocument = final_output.indexOf("Document");
let indexOfClosingDocument = final_output.indexOf(">", indexOfDocument) + 1;
let output = [
final_output.slice(0, indexOfClosingDocument),
colorElement,
final_output.slice(indexOfClosingDocument)
].join('');
// Add reference to style element
let indexOfPlacemark = output.indexOf('Placemark id="' + idToExport + '"');
let indexOfClosingPlacemark = output.indexOf(">", indexOfPlacemark) + 1;
output = [
output.slice(0, indexOfClosingPlacemark),
'<styleUrl>#app_style_'+idToExport+'</styleUrl>',
output.slice(indexOfClosingPlacemark)
].join('');
final_output = output;
}
this.mainDoc = "data:text/json;charset=utf-8," + final_output;
console.log(this.mainDoc); // <-- Here I can see the whole document perfectly fine
let link = document.createElement("a");
link.download = this.file_name + this.file_extension;
link.href = this.mainDoc;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
link = null;
}
All variables are correctly obtained and the file end in a word the middle of the text, without relation to any variable.
Originally the method I used for editing the file was jQuery.parseXML() and the same error happened, so I tried to change the method to this one I posted above.
I imagine that the problem may be some asynchronous step that is still in progress when the download event is triggered, but analyzing the code that was passed in I can’t see any asynchronous part.
I tried to use FileSaver.js as an alternative method to download the file, but the same error happened.
I tried to encapsulate this part in a Promise
to be sure nothing was being left behind, but this didn’t solve the issue either.
myDownloadFunction(file: Features[]) {
...
// Feature is OpenLayer's Feature
// https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html
// Declare variables and minor changes
let final_output:string = kml_format.writeFeatures(file);
...
// Add references
this.addReference(final_output).then(fo2 => {
this.mainDoc = "data:text/json;charset=utf-8," + fo2;
let link = document.createElement("a");
link.download = this.file_name + this.file_extension;
link.href = fo2;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
link = null;
});
}
addReference(final_output): Promise<string> {
return new Promise((resolve, reject) => {
this.featuresToExport.forEach((feature, index, arr) => {
let idToExport = feature.id_;
let featureColor:string = feature.values_.color;
let featureHexColor = this.getColorByName(featureColor);
console.table({"idToExport": idToExport, "featureColor": featureColor, "featureHexColor": featureHexColor});
let colorElement = '<Style id="sfmapp_style_'+idToExport+'"><IconStyle><Icon><href>https://earth.google.com/earth/rpc/cc/icon?color='+featureHexColor+'&id=2000&scale=4</href></Icon><hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/></IconStyle></Style>';
// Add style element
let indexOfDocument = final_output.indexOf("Document");
let indexOfClosingDocument = final_output.indexOf(">", indexOfDocument) + 1;
let output = [
final_output.slice(0, indexOfClosingDocument),
colorElement,
final_output.slice(indexOfClosingDocument)
].join('');
// Add reference to style element
let indexOfPlacemark = output.indexOf('Placemark id="' + idToExport + '"');
let indexOfClosingPlacemark = output.indexOf(">", indexOfPlacemark) + 1;
output = [
output.slice(0, indexOfClosingPlacemark),
'<styleUrl>#sfmapp_style_'+idToExport+'</styleUrl>',
output.slice(indexOfClosingPlacemark)
].join('');
final_output = output;
if (index === arr.length - 1){
resolve(final_output);
}
});
});
}
I think it’s important to point out that this method is working on the device, but it involves more steps, which is consistent with this theory of the asynchronicity problem.
Here you can see an example of how the file is suppose to be, and here how is being downloaded.
I have tried some other things and I think I have narrowed down to the source of the problem. When I remove the hashtag character (#) from the reference text, everything works as expected. If I leave the hashtag it breaks. Someone has a clue why this is happening? I tried to escape as we usually do (#) but that didn’t work.
let referenceElement = '<styleUrl>#app_style_'+idToExport+'</styleUrl>'; // It will break
let referenceElement = '<styleUrl>app_style_'+idToExport+'</styleUrl>'; // Working fine