Telecharger une ressource la telecharge, au lieu de l'ouvrir

L'attribut `download` d'un lien est ignore quand la cible est sur une autre
origine — Firebase Storage ici. Le navigateur se contentait donc d'ouvrir la
ressource dans un onglet, et le nom de fichier calcule par
`downloadFileNameFor` ne servait a rien.

Le fichier est d'abord recupere en blob et le lien pointe sur l'URL d'objet.
Repli sur l'ouverture en onglet si la requete echoue (CORS, reseau) : le
comportement d'avant, faute de mieux — a surveiller, c'est le chemin qui
reintroduit le bug sans rien dire.

Le telechargement groupe attend chaque fichier au lieu de lancer toute la
selection en parallele.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thomas Fransolet 2026-09-09 16:55:06 +02:00
parent 83eba7ff54
commit a1701e216a
2 changed files with 24 additions and 7 deletions

View File

@ -492,9 +492,9 @@ class _ResourceBodyGridState extends State<ResourceBodyGrid> {
TextButton.icon( TextButton.icon(
icon: const Icon(Icons.download, size: 16), icon: const Icon(Icons.download, size: 16),
label: Text(l.download, style: const TextStyle(fontSize: 12.5)), label: Text(l.download, style: const TextStyle(fontSize: 12.5)),
onPressed: () { onPressed: () async {
for (final resource in selected) { for (final resource in selected) {
downloadResource(resource); await downloadResource(resource);
} }
}, },
), ),

View File

@ -8,12 +8,29 @@ import 'resource_formatting.dart';
/// ///
/// L'ancienne modale forçait `'${label}.json'` quel que soit le type : un PNG /// L'ancienne modale forçait `'${label}.json'` quel que soit le type : un PNG
/// arrivait en `mon-image.json` et ne s'ouvrait nulle part sans renommage. /// arrivait en `mon-image.json` et ne s'ouvrait nulle part sans renommage.
void downloadResource(ResourceDTO resource) { ///
/// Le fichier est d'abord récupéré en blob : l'attribut `download` d'un lien est
/// ignoré quand la cible est sur une autre origine (Firebase Storage ici), et le
/// navigateur se contentait d'ouvrir la ressource dans un onglet. Si la requête
/// échoue (CORS, réseau), on retombe sur l'ouverture dans un nouvel onglet.
Future<void> downloadResource(ResourceDTO resource) async {
final url = resource.url; final url = resource.url;
if (url == null) return; if (url == null) return;
final anchor = html.AnchorElement(href: url) final fileName = downloadFileNameFor(resource);
..download = downloadFileNameFor(resource)
..target = '_blank'; try {
anchor.click(); final request = await html.HttpRequest.request(url, responseType: 'blob');
final blob = request.response as html.Blob;
final objectUrl = html.Url.createObjectUrlFromBlob(blob);
html.AnchorElement(href: objectUrl)
..download = fileName
..click();
html.Url.revokeObjectUrl(objectUrl);
} catch (_) {
html.AnchorElement(href: url)
..download = fileName
..target = '_blank'
..click();
}
} }