IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
소스 검색

Attempt to download inline but doesn't seem to work.

master
부모
커밋
589a1aece8
4개의 변경된 파일314개의 추가작업 그리고 4개의 파일을 삭제
  1. +1
    -1
      frontend/rollup.config.js
  2. +1
    -1
      frontend/src/js/page/wb-app.js
  3. +16
    -2
      frontend/src/js/page/wb-download-page.js
  4. +296
    -0
      frontend/src/js/thirdparty/StreamSaver.js

+ 1
- 1
frontend/rollup.config.js 파일 보기

@@ -49,7 +49,7 @@ export default {
'node_modules/js-yaml/dist/js-yaml.min.js': 'dist/js/js-yaml.min.js',
'node_modules/streamsaver/mitm.html': 'dist/mitm.html',
'node_modules/streamsaver/sw.js': 'dist/sw.js',
'node_modules/streamsaver/StreamSaver.js': 'dist/js/StreamSaver.js',
'src/js/thirdparty/StreamSaver.js': 'dist/js/StreamSaver.js',
'node_modules/web-streams-polyfill/dist/ponyfill.js': 'dist/js/web-streams-polyfill/ponyfill.js',
verbose: true
}),


+ 1
- 1
frontend/src/js/page/wb-app.js 파일 보기

@@ -93,7 +93,7 @@ class WBApp extends Component {
<WBLaunchWorkflowPage path="/workflow-launch/:workflowUuid" app={ this } />
<WBDownloadPage path="/download/:blocksBlobUrl" app={ this } />
<WBDownloadPage path="/download/:blocksBlobUrl/:inline?" app={ this } />
</Router>
);
}


+ 16
- 2
frontend/src/js/page/wb-download-page.js 파일 보기

@@ -1,9 +1,21 @@
import { h, Component } from 'preact';
import makeArvadosRequest from 'make-arvados-request';
function contentTypeFromFilename(name) {
let ext = name.split('.');
ext = ext[ext.length - 1].toUpperCase();
if (ext === 'TXT')
return 'text/plain; charset=utf-8';
if (ext === 'JPG' || ext === 'JPEG')
return 'image/jpeg';
if (ext === 'PNG')
return 'image/png';
return 'application/octet-stream; charset=utf-8';
}
class WBDownloadPage extends Component {
componentDidMount() {
const { app, blocksBlobUrl } = this.props;
const { app, blocksBlobUrl, inline } = this.props;
const { arvHost, arvToken } = app.state;
let prom = new Promise((accept, reject) => {
@@ -39,7 +51,9 @@ class WBDownloadPage extends Component {
[ _, _, name, file ] = JSON.parse(text);
fileStream = streamSaver.createWriteStream(name, {
size: file[1]
size: file[1],
inline: inline,
contentType: contentTypeFromFilename(name)
});
writer = fileStream.getWriter();


+ 296
- 0
frontend/src/js/thirdparty/StreamSaver.js 파일 보기

@@ -0,0 +1,296 @@
/* global chrome location ReadableStream define MessageChannel TransformStream */

;((name, definition) => {
typeof module !== 'undefined'
? module.exports = definition()
: typeof define === 'function' && typeof define.amd === 'object'
? define(definition)
: this[name] = definition()
})('streamSaver', () => {
'use strict'

let mitmTransporter = null
let supportsTransferable = false
const test = fn => { try { fn() } catch (e) {} }
const ponyfill = window.WebStreamsPolyfill || {}
const isSecureContext = window.isSecureContext
let useBlobFallback = /constructor/i.test(window.HTMLElement) || !!window.safari
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
? 'iframe'
: 'navigate'

const streamSaver = {
createWriteStream,
WritableStream: window.WritableStream || ponyfill.WritableStream,
supported: true,
version: { full: '2.0.0', major: 2, minor: 0, dot: 0 },
mitm: 'https://jimmywarting.github.io/StreamSaver.js/mitm.html?version=2.0.0'
}

/**
* create a hidden iframe and append it to the DOM (body)
*
* @param {string} src page to load
* @return {HTMLIFrameElement} page to load
*/
function makeIframe (src) {
if (!src) throw new Error('meh')
const iframe = document.createElement('iframe')
iframe.hidden = true
iframe.src = src
iframe.loaded = false
iframe.name = 'iframe'
iframe.isIframe = true
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
iframe.addEventListener('load', () => {
iframe.loaded = true
}, { once: true })
document.body.appendChild(iframe)
return iframe
}

/**
* create a popup that simulates the basic things
* of what a iframe can do
*
* @param {string} src page to load
* @return {object} iframe like object
*/
function makePopup (src) {
const options = 'width=200,height=100'
const delegate = document.createDocumentFragment()
const popup = {
frame: window.open(src, 'popup', options),
loaded: false,
isIframe: false,
isPopup: true,
remove () { popup.frame.close() },
addEventListener (...args) { delegate.addEventListener(...args) },
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
removeEventListener (...args) { delegate.removeEventListener(...args) },
postMessage (...args) { popup.frame.postMessage(...args) }
}

const onReady = evt => {
if (evt.source === popup.frame) {
popup.loaded = true
window.removeEventListener('message', onReady)
popup.dispatchEvent(new Event('load'))
}
}

window.addEventListener('message', onReady)

return popup
}

try {
// We can't look for service worker since it may still work on http
new Response(new ReadableStream())
if (isSecureContext && !('serviceWorker' in navigator)) {
useBlobFallback = true
}
} catch (err) {
useBlobFallback = true
}

test(() => {
// Transfariable stream was first enabled in chrome v73 behind a flag
const { readable } = new TransformStream()
const mc = new MessageChannel()
mc.port1.postMessage(readable, [readable])
mc.port1.close()
mc.port2.close()
supportsTransferable = true
// Freeze TransformStream object (can only work with native)
Object.defineProperty(streamSaver, 'TransformStream', {
configurable: false,
writable: false,
value: TransformStream
})
})

function loadTransporter () {
if (!mitmTransporter) {
mitmTransporter = isSecureContext
? makeIframe(streamSaver.mitm)
: makePopup(streamSaver.mitm)
}
}

/**
* @param {string} filename filename that should be used
* @param {object} options [description]
* @param {number} size depricated
* @return {WritableStream}
*/
function createWriteStream (filename, options, size) {
let opts = {
size: null,
pathname: null,
writableStrategy: undefined,
readableStrategy: undefined
}

// normalize arguments
if (Number.isFinite(options)) {
[ size, options ] = [ options, size ]
console.warn('[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream')
opts.size = size
opts.writableStrategy = options
} else if (options && options.highWaterMark) {
console.warn('[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream')
opts.size = size
opts.writableStrategy = options
} else {
opts = options || {}
}
if (!useBlobFallback) {
loadTransporter()

var bytesWritten = 0 // by StreamSaver.js (not the service worker)
var downloadUrl = null
var channel = new MessageChannel()

// Make filename RFC5987 compatible
filename = encodeURIComponent(filename.replace(/\//g, ':'))
.replace(/['()]/g, escape)
.replace(/\*/g, '%2A')

const response = {
transferringReadable: supportsTransferable,
pathname: opts.pathname || Math.random().toString().slice(-6) + '/' + filename,
headers: {
'Content-Type': (options.contentType ? options.contentType :
'application/octet-stream; charset=utf-8'),
'Content-Disposition': (options.inline ? 'inline' :
("attachment; filename*=UTF-8''" + filename))
}
}

if (opts.size) {
response.headers['Content-Length'] = opts.size
}

const args = [ response, '*', [ channel.port2 ] ]

if (supportsTransferable) {
const transformer = downloadStrategy === 'iframe' ? undefined : {
// This transformer & flush method is only used by insecure context.
transform (chunk, controller) {
bytesWritten += chunk.length
controller.enqueue(chunk)

if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
flush () {
if (downloadUrl) {
location.href = downloadUrl
}
}
}
var ts = new streamSaver.TransformStream(
transformer,
opts.writableStrategy,
opts.readableStrategy
)
const readableStream = ts.readable

channel.port1.postMessage({ readableStream }, [ readableStream ])
}

channel.port1.onmessage = evt => {
// Service worker sent us a link that we should open.
if (evt.data.download) {
// Special treatment for popup...
if (downloadStrategy === 'navigate') {
mitmTransporter.remove()
mitmTransporter = null
if (bytesWritten) {
location.href = evt.data.download
} else {
downloadUrl = evt.data.download
}
} else {
if (mitmTransporter.isPopup) {
mitmTransporter.remove()
// Special case for firefox, they can keep sw alive with fetch
if (downloadStrategy === 'iframe') {
makeIframe(streamSaver.mitm)
}
}

// We never remove this iframes b/c it can interrupt saving
makeIframe(evt.data.download)
}
}
}

if (mitmTransporter.loaded) {
mitmTransporter.postMessage(...args)
} else {
mitmTransporter.addEventListener('load', () => {
mitmTransporter.postMessage(...args)
}, { once: true })
}
}

let chunks = []

return (!useBlobFallback && ts && ts.writable) || new streamSaver.WritableStream({
write (chunk) {
if (useBlobFallback) {
// Safari... The new IE6
// https://github.com/jimmywarting/StreamSaver.js/issues/69
//
// even doe it has everything it fails to download anything
// that comes from the service worker..!
chunks.push(chunk)
return
}

// is called when a new chunk of data is ready to be written
// to the underlying sink. It can return a promise to signal
// success or failure of the write operation. The stream
// implementation guarantees that this method will be called
// only after previous writes have succeeded, and never after
// close or abort is called.

// TODO: Kind of important that service worker respond back when
// it has been written. Otherwise we can't handle backpressure
// EDIT: Transfarable streams solvs this...
channel.port1.postMessage(chunk)
bytesWritten += chunk.length

if (downloadUrl) {
location.href = downloadUrl
downloadUrl = null
}
},
close () {
if (useBlobFallback) {
const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8' })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
} else {
channel.port1.postMessage('end')
}
},
abort () {
chunks = []
channel.port1.postMessage('abort')
channel.port1.onmessage = null
channel.port1.close()
channel.port2.close()
channel = null
}
}, opts.writableStrategy)
}

return streamSaver
})

불러오는 중...
취소
저장