From 480b955cce646bb2438e751dbd4b99b3086b7771 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 16 Jan 2022 20:10:30 +0100 Subject: [PATCH 1/6] fix: use direct link to gateway to download files License: MIT Signed-off-by: Henrique Dias --- src/bundles/files/actions.js | 3 +-- src/files/FilesPage.js | 28 ++++------------------- src/files/download-file.js | 44 ------------------------------------ src/lib/files.js | 39 +++++++++++--------------------- 4 files changed, 19 insertions(+), 95 deletions(-) delete mode 100644 src/files/download-file.js diff --git a/src/bundles/files/actions.js b/src/bundles/files/actions.js index f33362cd9..a9660f52d 100644 --- a/src/bundles/files/actions.js +++ b/src/bundles/files/actions.js @@ -396,9 +396,8 @@ const actions = () => ({ * @param {FileStat[]} files */ doFilesDownloadLink: (files) => perform(ACTIONS.DOWNLOAD_LINK, async (ipfs, { store }) => { - const apiUrl = store.selectApiUrl() const gatewayUrl = store.selectGatewayUrl() - return await getDownloadLink(files, gatewayUrl, apiUrl, ipfs) + return await getDownloadLink(files, gatewayUrl, ipfs) }), /** diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index 456ae861d..11078a3af 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -6,7 +6,6 @@ import { withTranslation, Trans } from 'react-i18next' import ReactJoyride from 'react-joyride' // Lib import { filesTour } from '../lib/tours' -import downloadFile from './download-file' // Components import ContextMenu from './context-menu/ContextMenu' import withTour from '../components/tour/withTour' @@ -27,8 +26,6 @@ const FilesPage = ({ files, filesPathInfo, pinningServices, toursEnabled, handleJoyrideCallback, isCliTutorModeEnabled, cliOptions, t }) => { const contextMenuRef = useRef() - const [downloadAbort, setDownloadAbort] = useState(null) - const [downloadProgress, setDownloadProgress] = useState(null) const [modals, setModals] = useState({ show: null, files: null }) const [contextMenu, setContextMenu] = useState({ isOpen: false, @@ -58,28 +55,14 @@ const FilesPage = ({ */ const onDownload = async (files) => { - if (downloadProgress !== null) { - return downloadAbort() - } - - const { url, filename, method } = await doFilesDownloadLink(files) - - if (method === 'GET') { - const link = document.createElement('a') - link.href = url - link.click() - } else { - const updater = (v) => setDownloadProgress(v) - const { abort } = await downloadFile(url, filename, updater, method) - setDownloadAbort(() => abort) - } + const { url, filename } = await doFilesDownloadLink(files) + const link = document.createElement('a') + link.download = filename // TODO: filename isn't working. Keep getting 'get.tar.gz' - is it being overwritten? + link.href = url + link.click() } const onDownloadCar = async (files) => { - if (downloadProgress !== null) { - return downloadAbort() - } - const url = await doFilesDownloadCarLink(files) const link = document.createElement('a') link.href = url @@ -167,7 +150,6 @@ const FilesPage = ({ pendingPins={pendingPins} failedPins={failedPins} upperDir={files.upper} - downloadProgress={downloadProgress} onShare={(files) => showModal(SHARE, files)} onRename={(files) => showModal(RENAME, files)} onRemove={(files) => showModal(DELETE, files)} diff --git a/src/files/download-file.js b/src/files/download-file.js deleted file mode 100644 index 9d830b918..000000000 --- a/src/files/download-file.js +++ /dev/null @@ -1,44 +0,0 @@ -const downloadFile = (srcUrl, filename, progressCallback, method = 'GET') => { - const xhr = new window.XMLHttpRequest() - let total = 0 - - const abort = () => { - xhr.abort() - progressCallback(null) - } - - xhr.responseType = 'blob' - xhr.open(method, srcUrl, true) - - xhr.onload = (e) => { - progressCallback(100) - - const res = xhr.response - const blob = new window.Blob([res]) - const url = window.URL.createObjectURL(blob) - const a = document.createElement('a') - - document.body.appendChild(a) - a.style = 'display:none' - a.href = url - a.download = filename - a.click() - - window.URL.revokeObjectURL(url) - progressCallback(null) - } - - xhr.onprogress = (e) => { - total = e.lengthComputable - ? e.total - : (total || - xhr.getResponseHeader('X-Content-Length') || - xhr.getResponseHeader('Content-Length')) - - progressCallback((e.loaded / total) * 100) - } - - xhr.send() - return { abort } -} -export default downloadFile diff --git a/src/lib/files.js b/src/lib/files.js index 1aa37c62e..e0451a003 100644 --- a/src/lib/files.js +++ b/src/lib/files.js @@ -38,28 +38,23 @@ export function normalizeFiles (files) { * @typedef {Object} FileDownload * @property {string} url * @property {string} filename - * @property {string} method * * @param {FileStat} file * @param {string} gatewayUrl - * @param {string} apiUrl * @returns {Promise} */ -async function downloadSingle (file, gatewayUrl, apiUrl) { - let url, filename, method +async function downloadSingle (file, gatewayUrl) { + let url, filename if (file.type === 'directory') { - const name = file.name || `download_${file.cid}` // Name is not always available. - url = `${apiUrl}/api/v0/get?arg=${file.cid}&archive=true&compress=true` - filename = `${name}.tar.gz` - method = 'POST' // API is POST-only + filename = `${file.name || `download_${file.cid}`}.tar.gz` + url = `${gatewayUrl}/api/v0/get?arg=${file.cid}&archive=true&compress=true` } else { - url = `${gatewayUrl}/ipfs/${file.cid}?download=true&filename=${file.name}` filename = file.name - method = 'GET' + url = `${gatewayUrl}/ipfs/${file.cid}?download=true&filename=${file.name}` } - return { url, filename, method } + return { url, filename } } /** @@ -90,22 +85,15 @@ export async function makeCIDFromFiles (files, ipfs) { /** * * @param {FileStat[]} files - * @param {string} apiUrl + * @param {string} gatewayUrl * @param {IPFSService} ipfs * @returns {Promise} */ -async function downloadMultiple (files, apiUrl, ipfs) { - if (!apiUrl) { - const e = new Error('api url undefined') - return Promise.reject(e) - } - +async function downloadMultiple (files, gatewayUrl, ipfs) { const cid = await makeCIDFromFiles(files, ipfs) - return { - url: `${apiUrl}/api/v0/get?arg=${cid}&archive=true&compress=true`, - filename: `download_${cid}.tar.gz`, - method: 'POST' // API is POST-only + url: `${gatewayUrl}/api/v0/get?arg=${cid}&archive=true&compress=true`, + filename: `download_${cid}.tar.gz` } } @@ -113,16 +101,15 @@ async function downloadMultiple (files, apiUrl, ipfs) { * * @param {FileStat[]} files * @param {string} gatewayUrl - * @param {string} apiUrl * @param {IPFSService} ipfs * @returns {Promise} */ -export async function getDownloadLink (files, gatewayUrl, apiUrl, ipfs) { +export async function getDownloadLink (files, gatewayUrl, ipfs) { if (files.length === 1) { - return downloadSingle(files[0], gatewayUrl, apiUrl) + return downloadSingle(files[0], gatewayUrl) } - return downloadMultiple(files, apiUrl, ipfs) + return downloadMultiple(files, gatewayUrl, ipfs) } /** From b8ec0cfd3a9275a1382622eff75d1663bc99aff5 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 10 Nov 2022 14:26:27 +0100 Subject: [PATCH 2/6] feat: massively simplify file downloading --- src/files/FilesPage.js | 3 +-- src/lib/files.js | 46 ++++++++++++------------------------------ 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index 11078a3af..22246d29a 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -55,9 +55,8 @@ const FilesPage = ({ */ const onDownload = async (files) => { - const { url, filename } = await doFilesDownloadLink(files) + const url = await doFilesDownloadLink(files) const link = document.createElement('a') - link.download = filename // TODO: filename isn't working. Keep getting 'get.tar.gz' - is it being overwritten? link.href = url link.click() } diff --git a/src/lib/files.js b/src/lib/files.js index e0451a003..a6a96671b 100644 --- a/src/lib/files.js +++ b/src/lib/files.js @@ -35,26 +35,20 @@ export function normalizeFiles (files) { } /** - * @typedef {Object} FileDownload - * @property {string} url - * @property {string} filename - * - * @param {FileStat} file + * @param {string} type + * @param {string} name + * @param {CID} cid * @param {string} gatewayUrl - * @returns {Promise} + * @returns {string} */ -async function downloadSingle (file, gatewayUrl) { - let url, filename +function getDownloadURL (type, name, cid, gatewayUrl) { + const filename = `${name || `download_${cid.toString()}`}.tar` - if (file.type === 'directory') { - filename = `${file.name || `download_${file.cid}`}.tar.gz` - url = `${gatewayUrl}/api/v0/get?arg=${file.cid}&archive=true&compress=true` + if (type === 'directory') { + return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&format=tar&filename=${filename}` } else { - filename = file.name - url = `${gatewayUrl}/ipfs/${file.cid}?download=true&filename=${file.name}` + return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&filename=${filename}` } - - return { url, filename } } /** @@ -87,29 +81,15 @@ export async function makeCIDFromFiles (files, ipfs) { * @param {FileStat[]} files * @param {string} gatewayUrl * @param {IPFSService} ipfs - * @returns {Promise} - */ -async function downloadMultiple (files, gatewayUrl, ipfs) { - const cid = await makeCIDFromFiles(files, ipfs) - return { - url: `${gatewayUrl}/api/v0/get?arg=${cid}&archive=true&compress=true`, - filename: `download_${cid}.tar.gz` - } -} - -/** - * - * @param {FileStat[]} files - * @param {string} gatewayUrl - * @param {IPFSService} ipfs - * @returns {Promise} + * @returns {Promise} */ export async function getDownloadLink (files, gatewayUrl, ipfs) { if (files.length === 1) { - return downloadSingle(files[0], gatewayUrl) + return getDownloadURL(files[0].type, files[0].name, files[0].cid, gatewayUrl) } - return downloadMultiple(files, gatewayUrl, ipfs) + const cid = await makeCIDFromFiles(files, ipfs) + return getDownloadURL('directory', '', cid, gatewayUrl) } /** From d24c9486b3c17d55db83f383ac4b7a57c4a4a10d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 10 Nov 2022 14:28:05 +0100 Subject: [PATCH 3/6] fix: download filename --- src/lib/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/files.js b/src/lib/files.js index a6a96671b..0d9bb2969 100644 --- a/src/lib/files.js +++ b/src/lib/files.js @@ -42,11 +42,11 @@ export function normalizeFiles (files) { * @returns {string} */ function getDownloadURL (type, name, cid, gatewayUrl) { - const filename = `${name || `download_${cid.toString()}`}.tar` - if (type === 'directory') { + const filename = `${name || `download_${cid.toString()}`}.tar` return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&format=tar&filename=${filename}` } else { + const filename = `${name || cid}` return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&filename=${filename}` } } From 6bbb524fb994c4deafe90ec5a0f0defa77c26fe6 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 10 Nov 2022 14:32:45 +0100 Subject: [PATCH 4/6] refactor: remove download progress things --- src/files/files-list/FilesList.js | 4 +-- src/files/selected-actions/SelectedActions.js | 30 ++----------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/files/files-list/FilesList.js b/src/files/files-list/FilesList.js index 8bdafe277..476aaf707 100644 --- a/src/files/files-list/FilesList.js +++ b/src/files/files-list/FilesList.js @@ -51,7 +51,7 @@ const mergeRemotePinsIntoFiles = (files, remotePins = [], pendingPins = [], fail } export const FilesList = ({ - className, files, pins, pinningServices, remotePins, pendingPins, failedPins, filesSorting, updateSorting, downloadProgress, filesIsFetching, filesPathInfo, showLoadingAnimation, + className, files, pins, pinningServices, remotePins, pendingPins, failedPins, filesSorting, updateSorting, filesIsFetching, filesPathInfo, showLoadingAnimation, onShare, onSetPinning, onInspect, onDownload, onRemove, onRename, onNavigate, onRemotePinClick, onAddFiles, onMove, doFetchRemotePins, doDismissFailedPin, handleContextMenuClick, t }) => { const [selected, setSelected] = useState([]) @@ -355,7 +355,6 @@ export const FilesList = ({ inspect={() => onInspect(selectedFiles[0].cid)} count={selectedFiles.length} isMfs={filesPathInfo.isMfs} - downloadProgress={downloadProgress} size={selectedFiles.reduce((a, b) => a + (b.size || 0), 0)} /> } } @@ -374,7 +373,6 @@ FilesList.propTypes = { asc: PropTypes.bool.isRequired }), updateSorting: PropTypes.func.isRequired, - downloadProgress: PropTypes.number, filesIsFetching: PropTypes.bool, filesPathInfo: PropTypes.object, // Actions diff --git a/src/files/selected-actions/SelectedActions.js b/src/files/selected-actions/SelectedActions.js index a722dd9e5..09f7c4968 100644 --- a/src/files/selected-actions/SelectedActions.js +++ b/src/files/selected-actions/SelectedActions.js @@ -55,7 +55,6 @@ class SelectedActions extends React.Component { download: PropTypes.func.isRequired, rename: PropTypes.func.isRequired, inspect: PropTypes.func.isRequired, - downloadProgress: PropTypes.number, t: PropTypes.func.isRequired, tReady: PropTypes.bool.isRequired, isMfs: PropTypes.bool.isRequired, @@ -70,37 +69,12 @@ class SelectedActions extends React.Component { force100: false } - componentDidUpdate (prev) { - if (this.props.downloadProgress === 100 && prev.downloadProgress !== 100) { - this.setState({ force100: true }) - setTimeout(() => { - this.setState({ force100: false }) - }, 2000) - } - } - componentDidMount () { this.containerRef.current && this.containerRef.current.focus() } - get downloadText () { - if (this.state.force100) { - return this.props.t('finished') - } - - if (!this.props.downloadProgress) { - return this.props.t('app:actions.download') - } - - if (this.props.downloadProgress === 100) { - return this.props.t('finished') - } - - return this.props.downloadProgress.toFixed(0) + '%' - } - render () { - const { t, tReady, animateOnStart, count, size, unselect, remove, share, setPinning, download, downloadProgress, rename, inspect, className, style, isMfs, ...props } = this.props + const { t, tReady, animateOnStart, count, size, unselect, remove, share, setPinning, download, rename, inspect, className, style, isMfs, ...props } = this.props const isSingle = count === 1 @@ -131,7 +105,7 @@ class SelectedActions extends React.Component {