Skip to content
Advertisement

Hashtag character makes file incomplete when downloading

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[]) {
    ...
    // Features is OpenLayer's Features
    // 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+'&amp;id=2000&amp;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+'&amp;id=2000&amp;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); 
            }
        });
    });
}

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 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

Answer

Solved the problem by using the ASCII encoding reference for the hashtag character:

let referenceElement = '<styleUrl>%23app_style_'+idToExport+'</styleUrl>';
Advertisement