mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 15:38:15 +00:00
Zip download (#3252)
* [dir view] rewrote 'zip download' * renamed component 'ShareLinkZipDownloadDialog' to 'ZipDownloadDialog' * [zip download] show specific error msg
This commit is contained in:
@@ -1,127 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||
import { gettext, fileServerRoot } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import Loading from '../loading';
|
||||
|
||||
const propTypes = {
|
||||
token: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
toggleDialog: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
let interval;
|
||||
|
||||
class ShareLinkZipDownloadDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
errorMsg: '',
|
||||
zipProgress: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { token, path } = this.props;
|
||||
seafileAPI.getShareLinkZipTask(token, path).then((res) => {
|
||||
const zipToken = res.data['zip_token'];
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: '',
|
||||
zipToken: zipToken
|
||||
});
|
||||
this.queryZipProgress();
|
||||
interval = setInterval(this.queryZipProgress, 1000);
|
||||
}).catch((error) => {
|
||||
let errorMsg = '';
|
||||
if (error.response) {
|
||||
errorMsg = gettext('Error');
|
||||
} else {
|
||||
errorMsg = gettext('Please check the network.');
|
||||
}
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
queryZipProgress = () => {
|
||||
const zipToken = this.state.zipToken;
|
||||
seafileAPI.queryZipProgress(zipToken).then((res) => {
|
||||
const data = res.data;
|
||||
this.setState({
|
||||
zipProgress: data.total == 0 ? '100%' : (data.zipped/data.total*100).toFixed(2) + '%'
|
||||
});
|
||||
if (data['total'] == data['zipped']) {
|
||||
clearInterval(interval);
|
||||
this.props.toggleDialog();
|
||||
location.href = `${fileServerRoot}zip/${zipToken}`;
|
||||
}
|
||||
}).catch((error) => {
|
||||
clearInterval(interval);
|
||||
let errorMsg = '';
|
||||
if (error.response) {
|
||||
errorMsg = gettext('Error');
|
||||
} else {
|
||||
errorMsg = gettext('Please check the network.');
|
||||
}
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cancelZipTask = () => {
|
||||
const zipToken = this.state.zipToken;
|
||||
seafileAPI.cancelZipTask(zipToken).then((res) => {
|
||||
// do nothing
|
||||
}).catch((error) => {
|
||||
// do nothing
|
||||
});
|
||||
}
|
||||
|
||||
toggleDialog = () => {
|
||||
const zipProgress = this.state.zipProgress;
|
||||
if (zipProgress && zipProgress != '100%') {
|
||||
clearInterval(interval);
|
||||
this.cancelZipTask();
|
||||
}
|
||||
this.props.toggleDialog();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={true} centered={true} toggle={this.toggleDialog}>
|
||||
<ModalHeader toggle={this.toggleDialog}>{gettext('Download')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Content data={this.state} />
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Content extends React.Component {
|
||||
|
||||
render() {
|
||||
const {isLoading, errorMsg, zipProgress} = this.props.data;
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
return <p className="error mt-4 text-center">{errorMsg}</p>;
|
||||
}
|
||||
|
||||
return <p className="mt-4 text-center">{`${gettext('Packaging...')} ${zipProgress}`}</p>;
|
||||
}
|
||||
}
|
||||
|
||||
ShareLinkZipDownloadDialog.propTypes = propTypes;
|
||||
|
||||
export default ShareLinkZipDownloadDialog;
|
@@ -1,30 +1,135 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||
import { gettext, fileServerRoot } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import Loading from '../loading';
|
||||
|
||||
const propTypes = {
|
||||
onCancelDownload: PropTypes.func.isRequired,
|
||||
progress: PropTypes.number.isRequired,
|
||||
token: PropTypes.string,
|
||||
path: PropTypes.string.isRequired,
|
||||
repoID: PropTypes.string,
|
||||
target: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array
|
||||
]),
|
||||
toggleDialog: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class ZipDownloadDialog extends React.Component {
|
||||
let interval;
|
||||
|
||||
toggle = () => {
|
||||
this.props.onCancelDownload();
|
||||
class ZipDownloadDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
errorMsg: '',
|
||||
zipProgress: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { token, path, repoID, target } = this.props;
|
||||
const getZipTask = token ?
|
||||
seafileAPI.getShareLinkZipTask(token, path) :
|
||||
seafileAPI.zipDownload(repoID, path, target);
|
||||
getZipTask.then((res) => {
|
||||
const zipToken = res.data['zip_token'];
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: '',
|
||||
zipToken: zipToken
|
||||
});
|
||||
this.queryZipProgress();
|
||||
interval = setInterval(this.queryZipProgress, 1000);
|
||||
}).catch((error) => {
|
||||
let errorMsg = '';
|
||||
if (error.response) {
|
||||
errorMsg = error.response.data.error_msg || gettext('Error');
|
||||
} else {
|
||||
errorMsg = gettext('Please check the network.');
|
||||
}
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
queryZipProgress = () => {
|
||||
const zipToken = this.state.zipToken;
|
||||
seafileAPI.queryZipProgress(zipToken).then((res) => {
|
||||
const data = res.data;
|
||||
this.setState({
|
||||
zipProgress: data.total == 0 ? '100%' : (data.zipped/data.total*100).toFixed(2) + '%'
|
||||
});
|
||||
if (data['total'] == data['zipped']) {
|
||||
clearInterval(interval);
|
||||
this.props.toggleDialog();
|
||||
location.href = `${fileServerRoot}zip/${zipToken}`;
|
||||
}
|
||||
}).catch((error) => {
|
||||
clearInterval(interval);
|
||||
let errorMsg = '';
|
||||
if (error.response) {
|
||||
errorMsg = gettext('Error');
|
||||
} else {
|
||||
errorMsg = gettext('Please check the network.');
|
||||
}
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
errorMsg: errorMsg
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cancelZipTask = () => {
|
||||
const zipToken = this.state.zipToken;
|
||||
seafileAPI.cancelZipTask(zipToken).then((res) => {
|
||||
// do nothing
|
||||
}).catch((error) => {
|
||||
// do nothing
|
||||
});
|
||||
}
|
||||
|
||||
toggleDialog = () => {
|
||||
const zipProgress = this.state.zipProgress;
|
||||
if (zipProgress && zipProgress != '100%') {
|
||||
clearInterval(interval);
|
||||
this.cancelZipTask();
|
||||
}
|
||||
this.props.toggleDialog();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
<ModalHeader toggle={this.toggle}></ModalHeader>
|
||||
<Modal isOpen={true} centered={true} toggle={this.toggleDialog}>
|
||||
<ModalHeader toggle={this.toggleDialog}>{gettext('Download')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div>{this.props.progress + '%'}</div>
|
||||
<Content data={this.state} />
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Content extends React.Component {
|
||||
|
||||
render() {
|
||||
const {isLoading, errorMsg, zipProgress} = this.props.data;
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
return <p className="error mt-4 text-center">{errorMsg}</p>;
|
||||
}
|
||||
|
||||
return <p className="mt-4 text-center">{`${gettext('Packaging...')} ${zipProgress}`}</p>;
|
||||
}
|
||||
}
|
||||
|
||||
ZipDownloadDialog.propTypes = propTypes;
|
||||
|
||||
export default ZipDownloadDialog;
|
||||
|
@@ -9,11 +9,10 @@ import URLDecorator from '../../utils/url-decorator';
|
||||
import DirentMenu from './dirent-menu';
|
||||
import Rename from '../rename';
|
||||
import ModalPortal from '../modal-portal';
|
||||
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||
import MoveDirentDialog from '../dialog/move-dirent-dialog';
|
||||
import CopyDirentDialog from '../dialog/copy-dirent-dialog';
|
||||
import ShareDialog from '../dialog/share-dialog';
|
||||
import toaster from '../toast';
|
||||
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||
|
||||
import '../../css/dirent-list-item.css';
|
||||
|
||||
@@ -51,8 +50,7 @@ class DirentListItem extends React.Component {
|
||||
this.state = {
|
||||
isOperationShow: false,
|
||||
highlight: false,
|
||||
progress: 0,
|
||||
isProgressDialogShow: false,
|
||||
isZipDialogOpen: false,
|
||||
isMoveDialogShow: false,
|
||||
isCopyDialogShow: false,
|
||||
isShareDialogShow: false,
|
||||
@@ -61,7 +59,6 @@ class DirentListItem extends React.Component {
|
||||
isDragTipShow: false,
|
||||
isDropTipshow: false,
|
||||
};
|
||||
this.zipToken = null;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@@ -296,16 +293,8 @@ class DirentListItem extends React.Component {
|
||||
let repoID = this.props.repoID;
|
||||
let direntPath = this.getDirentPath(dirent);
|
||||
if (dirent.type === 'dir') {
|
||||
this.setState({isProgressDialogShow: true, progress: 0});
|
||||
seafileAPI.zipDownload(repoID, this.props.path, dirent.name).then(res => {
|
||||
this.zipToken = res.data['zip_token'];
|
||||
this.addDownloadAnimation();
|
||||
this.interval = setInterval(this.addDownloadAnimation, 1000);
|
||||
}).catch((error) => {
|
||||
clearInterval(this.interval);
|
||||
this.setState({isProgressDialogShow: false});
|
||||
let errorMessage = error.response.data.error_msg;
|
||||
toaster.danger(errorMessage);
|
||||
this.setState({
|
||||
isZipDialogOpen: true
|
||||
});
|
||||
} else {
|
||||
let url = URLDecorator.getUrl({type: 'download_file_url', repoID: repoID, filePath: direntPath});
|
||||
@@ -313,34 +302,9 @@ class DirentListItem extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
addDownloadAnimation = () => {
|
||||
let _this = this;
|
||||
let token = this.zipToken;
|
||||
seafileAPI.queryZipProgress(token).then(res => {
|
||||
let data = res.data;
|
||||
let progress = data.total === 0 ? 100 : (data.zipped / data.total * 100).toFixed(0);
|
||||
this.setState({progress: parseInt(progress)});
|
||||
|
||||
if (data['total'] === data['zipped']) {
|
||||
this.setState({
|
||||
progress: 100
|
||||
});
|
||||
clearInterval(this.interval);
|
||||
location.href = URLDecorator.getUrl({type: 'download_dir_zip_url', token: token});
|
||||
setTimeout(function() {
|
||||
_this.setState({isProgressDialogShow: false});
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onCancelDownload = () => {
|
||||
let zipToken = this.zipToken;
|
||||
seafileAPI.cancelZipTask(zipToken).then(res => {
|
||||
clearInterval(this.interval);
|
||||
this.setState({
|
||||
isProgressDialogShow: false,
|
||||
});
|
||||
closeZipDialog = () => {
|
||||
this.setState({
|
||||
isZipDialogOpen: false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -364,7 +328,7 @@ class DirentListItem extends React.Component {
|
||||
let dragStartItemData = {nodeDirent: this.props.dirent, nodeParentPath: this.props.path, nodeRootPath: nodeRootPath};
|
||||
dragStartItemData = JSON.stringify(dragStartItemData);
|
||||
|
||||
e.dataTransfer.effectAllowed = "move";
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setDragImage(this.refs.drag_icon, 15, 15);
|
||||
e.dataTransfer.setData('applicaiton/drag-item-info', dragStartItemData);
|
||||
}
|
||||
@@ -560,11 +524,13 @@ class DirentListItem extends React.Component {
|
||||
/>
|
||||
</ModalPortal>
|
||||
}
|
||||
{this.state.isProgressDialogShow &&
|
||||
{this.state.isZipDialogOpen &&
|
||||
<ModalPortal>
|
||||
<ZipDownloadDialog
|
||||
progress={this.state.progress}
|
||||
onCancelDownload={this.onCancelDownload}
|
||||
repoID={this.props.repoID}
|
||||
path={this.props.path}
|
||||
target={this.props.dirent.name}
|
||||
toggleDialog={this.closeZipDialog}
|
||||
/>
|
||||
</ModalPortal>
|
||||
}
|
||||
|
@@ -1,18 +1,17 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button , ButtonGroup , Modal } from 'reactstrap';
|
||||
import { Button, ButtonGroup } from 'reactstrap';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import URLDecorator from '../../utils/url-decorator';
|
||||
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||
import MoveDirentDialog from '../dialog/move-dirent-dialog';
|
||||
import CopyDirentDialog from '../dialog/copy-dirent-dialog';
|
||||
import DirentsMenu from '../dirent-list-view/dirents-menu';
|
||||
import ShareDialog from '../dialog/share-dialog';
|
||||
import RelatedFileDialogs from '../dialog/related-file-dialogs';
|
||||
import EditFileTagDialog from '../dialog/edit-filetag-dialog';
|
||||
import toaster from '../toast';
|
||||
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||
import ModalPortal from '../modal-portal';
|
||||
|
||||
const propTypes = {
|
||||
@@ -37,8 +36,7 @@ class MutipleDirOperationToolbar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
progress: 0,
|
||||
isProgressDialogShow: false,
|
||||
isZipDialogOpen: false,
|
||||
isMoveDialogShow: false,
|
||||
isCopyDialogShow: false,
|
||||
isMutipleOperation: true,
|
||||
@@ -50,7 +48,6 @@ class MutipleDirOperationToolbar extends React.Component {
|
||||
showRelatedFileDialog: false,
|
||||
viewMode: 'list_related_file',
|
||||
};
|
||||
this.zipToken = null;
|
||||
}
|
||||
|
||||
onMoveToggle = () => {
|
||||
@@ -74,49 +71,15 @@ class MutipleDirOperationToolbar extends React.Component {
|
||||
location.href= url;
|
||||
return;
|
||||
}
|
||||
let selectedDirentNames = selectedDirentList.map(dirent => {
|
||||
return dirent.name;
|
||||
});
|
||||
this.setState({isProgressDialogShow: true, progress: 0});
|
||||
seafileAPI.zipDownload(repoID, path, selectedDirentNames).then(res => {
|
||||
this.zipToken = res.data['zip_token'];
|
||||
this.addDownloadAnimation();
|
||||
this.interval = setInterval(this.addDownloadAnimation, 1000);
|
||||
}).catch((error) => {
|
||||
clearInterval(this.interval);
|
||||
this.setState({isProgressDialogShow: false});
|
||||
let errorMessage = error.response.data.error_msg;
|
||||
toaster.danger(errorMessage);
|
||||
this.setState({
|
||||
isZipDialogOpen: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addDownloadAnimation = () => {
|
||||
let _this = this;
|
||||
let token = this.zipToken;
|
||||
seafileAPI.queryZipProgress(token).then(res => {
|
||||
let data = res.data;
|
||||
let progress = data.total === 0 ? 100 : (data.zipped / data.total * 100).toFixed(0);
|
||||
this.setState({progress: parseInt(progress)});
|
||||
|
||||
if (data['total'] === data['zipped']) {
|
||||
this.setState({
|
||||
progress: 100
|
||||
});
|
||||
clearInterval(this.interval);
|
||||
location.href = URLDecorator.getUrl({type: 'download_dir_zip_url', token: token});
|
||||
setTimeout(function() {
|
||||
_this.setState({isProgressDialogShow: false});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
onCancelDownload = () => {
|
||||
seafileAPI.cancelZipTask(this.zipToken).then(() => {
|
||||
clearInterval(this.interval);
|
||||
this.setState({isProgressDialogShow: false});
|
||||
closeZipDialog = () => {
|
||||
this.setState({
|
||||
isZipDialogOpen: false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -315,8 +278,15 @@ class MutipleDirOperationToolbar extends React.Component {
|
||||
onCancelCopy={this.onCopyToggle}
|
||||
/>
|
||||
}
|
||||
{this.state.isProgressDialogShow &&
|
||||
<ZipDownloadDialog progress={this.state.progress} onCancelDownload={this.onCancelDownload} />
|
||||
{this.state.isZipDialogOpen &&
|
||||
<ModalPortal>
|
||||
<ZipDownloadDialog
|
||||
repoID={this.props.repoID}
|
||||
path={this.props.path}
|
||||
target={this.props.selectedDirentList.map(dirent => dirent.name)}
|
||||
toggleDialog={this.closeZipDialog}
|
||||
/>
|
||||
</ModalPortal>
|
||||
}
|
||||
{this.state.showLibContentViewDialogs && (
|
||||
<Fragment>
|
||||
|
@@ -9,7 +9,7 @@ import { seafileAPI } from './utils/seafile-api';
|
||||
import Loading from './components/loading';
|
||||
import toaster from './components/toast';
|
||||
import ModalPortal from './components/modal-portal';
|
||||
import ShareLinkZipDownloadDialog from './components/dialog/share-link-zip-download-dialog';
|
||||
import ZipDownloadDialog from './components/dialog/zip-download-dialog';
|
||||
import ImageDialog from './components/dialog/image-dialog';
|
||||
|
||||
import './css/shared-dir-view.css';
|
||||
@@ -228,7 +228,7 @@ class SharedDirView extends React.Component {
|
||||
</div>
|
||||
{this.state.isZipDialogOpen &&
|
||||
<ModalPortal>
|
||||
<ShareLinkZipDownloadDialog
|
||||
<ZipDownloadDialog
|
||||
token={token}
|
||||
path={this.state.zipFolderPath}
|
||||
toggleDialog={this.closeZipDialog}
|
||||
|
@@ -10,9 +10,6 @@ class URLDecorator {
|
||||
params = 'p=' + options.filePath;
|
||||
url = siteRoot + 'repo/' + historyRepoID + '/' + options.objID + '/download?' + params;
|
||||
break;
|
||||
case 'download_dir_zip_url':
|
||||
url = fileServerRoot + 'zip/' + options.token;
|
||||
break;
|
||||
case 'download_file_url':
|
||||
url = siteRoot + 'lib/' + options.repoID + '/file' + Utils.encodePath(options.filePath) + '?dl=1';
|
||||
break;
|
||||
|
@@ -94,7 +94,7 @@ class ZipTaskView(APIView):
|
||||
repo.store_id, repo.version, dir_id)
|
||||
|
||||
if dir_size > seaserv.MAX_DOWNLOAD_DIR_SIZE:
|
||||
error_msg = 'Unable to download directory "%s": size is too large.' % dir_name
|
||||
error_msg = _('Unable to download directory "%s": size is too large.') % dir_name
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
fake_obj_id = {
|
||||
|
Reference in New Issue
Block a user