From 4ab62c7d2e6c3c03b6527ed090d285439ae26400 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Tue, 13 Nov 2018 16:00:18 -0800 Subject: [PATCH] =?UTF-8?q?graph:=20fix=20=E2=80=9CDownload=20PNG=E2=80=9D?= =?UTF-8?q?=20for=20large=20images?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: When viewing large graphs in Chrome, clicking the “Download PNG” button could the error message, “Failed - Network error”. This is because Chrome does not support downloading large data-URLs. Chrome does, however, support downloading large object URLs, so this patch makes the appropriate change to use that API instead. Test Plan: Generate the `audio_demo` data and open the `wave:01;sine_wave` graph in TensorBoard in Chrome. Transitively expand all node groups. Then, click “Download PNG”. Before this patch, this consistently failed to download. After this patch, it downloads and image with the correct contents. Also tested in Firefox. wchargin-branch: graph-download-png-blob --- .../plugins/graph/tf_graph_common/minimap.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tensorboard/plugins/graph/tf_graph_common/minimap.ts b/tensorboard/plugins/graph/tf_graph_common/minimap.ts index 7a0e04069d..c9ee59ccc7 100644 --- a/tensorboard/plugins/graph/tf_graph_common/minimap.ts +++ b/tensorboard/plugins/graph/tf_graph_common/minimap.ts @@ -155,7 +155,24 @@ export class Minimap { let $download = d3.select('#graphdownload'); this.download = $download.node(); $download.on('click', d => { - this.download.href = this.downloadCanvas.toDataURL('image/png'); + // Revoke the old URL, if any. Then, generate a new URL. + URL.revokeObjectURL(this.download.href); + // We can't use the `HTMLCanvasElement.toBlob` API because it does + // not have a synchronous variant, and we need to update this href + // synchronously. Instead, we create a blob manually from the data + // URL. + const dataUrl = this.downloadCanvas.toDataURL("image/png"); + const prefix = dataUrl.slice(0, dataUrl.indexOf(",")); + if (!prefix.endsWith(";base64")) { + console.warn(`non-base64 data URL (${prefix}); cannot use blob download`); + this.download.href = dataUrl; + return; + } + const data = atob(dataUrl.slice(dataUrl.indexOf(",") + 1)); + const bytes = + new Uint8Array(data.length).map((_, i) => data.charCodeAt(i)); + const blob = new Blob([bytes], {type: "image/png"}); + this.download.href = URL.createObjectURL(blob); }); let $svg = d3.select(this.svg);