Storage from_path storing and downloading a file

HI! I’m having some issue with downloading a file I write into Storage: I can download it with its url, but the file downloaded has no file extensions. Could you help see if and how I can change that, by changing the code, or maybe the app logic? Many thanks in advance!

The app:

It’s about generative design in early building design phase. The user:
(1) uploads an .ifc file of the building location,
(2) sets their preferences on the building to be designed,
(3) views a bunch of possible building designs (as building blocks), and
(4) downloads the building designs they are interested in as .ifc files.

How I’m doing it now:

1. Saving .ifc files to storage

In step (3), when the user enters and updates the viktor page, the generation of building options are triggered. After all options are generated, one .ifc file is written for each option en the files are written into storage with:

# here for each building option, an ifc file is written into local directory as "example_building.ifc"
path_file = File.from_path(Path(__file__).parent / "example_building.ifc")
Storage().set("ifc_as_str_" + building.name  + ".ifc", data=path_file, scope='entity') 

# get the url of the file
file_obj = Storage().get("ifc_as_str_" + building.name + ".ifc", scope='entity')
print(file_obj.source)

2. Downloading the .ifc files from storage

(Still in step (3)) The user sees a webview, with a series of 2d plots showing all the building options. It’s created with jinja template.

# combine the url's with other data to be passed to the template
combined_list = [{'fig_3d': fig, 'detail_text_field': text, 'storage_url': url} for fig, text, url in zip(figs_3d, building_detail_text_fields, storage_url)]
# ... some other code in between...
with open(Path(__file__).parent / "plots.html.jinja", "rb") as template:
            result = render_jinja_template(
                template,
                dict(
                    figs=figs,
                    fig_location=fig_location,
                    building_descriptions=building_descriptions,
                    data=building_data,
                    combined_list=combined_list,  # the url's of the files are passed inside this list
                    legend=legend_img_str,
                ),
)

The download button inside the jinja template:

{% for item in combined_list %}
   <a href="{{ item.storage_url }}" download="ifc_as_str_{{ loop.index }}.ifc" class="button-link">Download2.ifc</a>
{% endfor %}

Question:

For now, the downloaded file would have some name like “d27b5e91-01ea-462f-82fa-e4d82dfc086c”, with no file extension. Ideally, the file should have the name with format “ifc_as_str_” + building.name + “.ifc”, with “.ifc” as file extension. What should I do differently?

Hey there!

The download parameter only works when downloading files on the same origin, which might be causing your code to not work as expected (<a>: The Anchor element - HTML: HyperText Markup Language | MDN).

To get around this, you could try downloading the file as a blob first using a fetch GET request, then triggering a download by using URL.createObjectURL and a dummy anchor element that you trigger a click on via JS.

Something like (not tested):

async function downloadFile(url) {
	const response = await fetch(url);
	const blob = response.blob();
	const href = URL.createObjectURL(blob);
	
	const a = document.createElement('a');
	a.href = href;
	a.download = 'your-filename.ifc';
	a.click();
	
	// at some point after the click you should clean up memory by using: URL.revokeObjectURL(a.href);
}
1 Like

Thanks for your answer Tom!

For others interested, the following code works for me:

<button onclick="downloadFile('{{ item.storage_url }}', 'optie_{{ loop.index }}.ifc')" class="button-link">Download IFC bestand </button>
<script>
// Function to download a file from a URL
    async function downloadFile(url, filename) { 
       try {
           const response = await fetch(url);
           if (!response.ok) {
               throw new Error('Network response was not ok');
           }
           const blob = await response.blob();
           const href = URL.createObjectURL(blob);

           // Create a dummy anchor element to trigger the download
           const a = document.createElement('a');
           a.href = href;
           a.download = filename; 
           document.body.appendChild(a);
           a.click();
           document.body.removeChild(a);

           // Clean up the object URL
           URL.revokeObjectURL(href);
       } catch (error) {
           console.error('There was an error downloading the file:', error);
       }
   }
</script>