2022-02-28 16:08:49 +00:00
|
|
|
import React from 'react';
|
|
|
|
import { Link, useParams } from 'react-router-dom';
|
2022-02-28 20:31:26 +00:00
|
|
|
import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3';
|
2022-02-28 16:08:49 +00:00
|
|
|
|
2022-02-28 16:59:06 +00:00
|
|
|
import Alert from 'react-bootstrap/Alert';
|
|
|
|
import Breadcrumb from 'react-bootstrap/Breadcrumb';
|
2022-02-28 20:31:26 +00:00
|
|
|
import Button from 'react-bootstrap/Button';
|
|
|
|
import Container from 'react-bootstrap/Container';
|
2022-02-28 16:59:06 +00:00
|
|
|
import Card from 'react-bootstrap/Card';
|
|
|
|
import ListGroup from 'react-bootstrap/ListGroup';
|
2022-02-28 20:31:26 +00:00
|
|
|
import Stack from 'react-bootstrap/Stack';
|
2022-02-28 16:59:06 +00:00
|
|
|
import { LinkContainer } from 'react-router-bootstrap'
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
import ObjectInfo from './ObjectInfo';
|
|
|
|
import UploadFiles from './UploadFiles';
|
|
|
|
import downloadFile from './downloadFile';
|
|
|
|
|
|
|
|
import { BsFolder, BsFileEarmarkText, BsDownload } from 'react-icons/bs';
|
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
type Props = {
|
|
|
|
client: S3Client;
|
|
|
|
bucket: string;
|
|
|
|
prefix: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
type State = {
|
2022-02-28 16:15:39 +00:00
|
|
|
loaded: boolean;
|
2022-02-28 16:08:49 +00:00
|
|
|
folders: string[];
|
|
|
|
files: string[];
|
2022-02-28 20:31:26 +00:00
|
|
|
info: string | null;
|
|
|
|
iInfo: number;
|
|
|
|
iUpload: number;
|
2022-02-28 16:08:49 +00:00
|
|
|
};
|
|
|
|
|
2022-02-28 16:59:06 +00:00
|
|
|
var cache: { [path: string]: State; } = {};
|
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
class ObjectList extends React.Component<Props, State> {
|
|
|
|
state = {
|
2022-02-28 16:15:39 +00:00
|
|
|
loaded: false,
|
2022-02-28 16:08:49 +00:00
|
|
|
folders: [],
|
|
|
|
files: [],
|
2022-02-28 20:31:26 +00:00
|
|
|
info: null,
|
|
|
|
iInfo: 0,
|
|
|
|
iUpload: 0,
|
2022-02-28 16:08:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
}
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
componentDidMount() {
|
|
|
|
//console.log(this.props);
|
2022-02-28 16:08:49 +00:00
|
|
|
|
2022-02-28 16:59:06 +00:00
|
|
|
if (cache[this.path()]) {
|
|
|
|
this.setState(cache[this.path()]);
|
|
|
|
}
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
this.loadEntries();
|
|
|
|
}
|
|
|
|
|
|
|
|
async loadEntries() {
|
2022-02-28 16:08:49 +00:00
|
|
|
let command = new ListObjectsV2Command({
|
|
|
|
Bucket: this.props.bucket,
|
|
|
|
Prefix: this.props.prefix,
|
|
|
|
Delimiter: '/',
|
|
|
|
});
|
|
|
|
try {
|
2022-02-28 16:15:39 +00:00
|
|
|
const pxlen = this.props.prefix.length;
|
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
const data = await this.props.client.send(command);
|
2022-02-28 20:31:26 +00:00
|
|
|
//console.log("ok", data);
|
2022-02-28 16:59:06 +00:00
|
|
|
|
|
|
|
const folders = (data.CommonPrefixes || []).map((cp) => cp.Prefix!.substring(pxlen));
|
|
|
|
const files = (data.Contents || []).map((obj) => obj.Key!.substring(pxlen));
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
folders.sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}));
|
|
|
|
files.sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}));
|
2022-02-28 16:59:06 +00:00
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
this.setState({
|
2022-02-28 16:15:39 +00:00
|
|
|
loaded: true,
|
2022-02-28 16:59:06 +00:00
|
|
|
folders: folders,
|
|
|
|
files: files,
|
2022-02-28 16:08:49 +00:00
|
|
|
});
|
2022-02-28 16:59:06 +00:00
|
|
|
cache[this.path()] = this.state;
|
2022-02-28 16:08:49 +00:00
|
|
|
} catch(error) {
|
|
|
|
console.log("err", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 16:59:06 +00:00
|
|
|
path() {
|
|
|
|
return this.props.bucket + "/" + this.props.prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderBreadcrumbs() {
|
2022-02-28 16:15:39 +00:00
|
|
|
let spl = this.props.prefix.split("/");
|
2022-02-28 16:59:06 +00:00
|
|
|
let items = [];
|
|
|
|
for (var i = 0; i < spl.length - 1; i++) {
|
2022-02-28 20:31:26 +00:00
|
|
|
let to = "/" + this.props.bucket + "/" + spl.slice(0, i+1).join("/") + "/" ;
|
2022-02-28 16:59:06 +00:00
|
|
|
if (i < spl.length - 2) {
|
|
|
|
items.push(
|
2022-02-28 20:31:26 +00:00
|
|
|
<LinkContainer to={to} key={to}>
|
2022-02-28 16:59:06 +00:00
|
|
|
<Breadcrumb.Item>{ spl[i] }</Breadcrumb.Item>
|
|
|
|
</LinkContainer>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
items.push(
|
2022-02-28 20:31:26 +00:00
|
|
|
<Breadcrumb.Item active key={to}>{ spl[i] }</Breadcrumb.Item>
|
2022-02-28 16:59:06 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-02-28 16:15:39 +00:00
|
|
|
return (
|
2022-02-28 16:59:06 +00:00
|
|
|
<Breadcrumb>
|
2022-02-28 20:31:26 +00:00
|
|
|
<LinkContainer key="BUCKETS" to="/">
|
2022-02-28 16:59:06 +00:00
|
|
|
<Breadcrumb.Item>my buckets</Breadcrumb.Item>
|
|
|
|
</LinkContainer>
|
|
|
|
{ this.props.prefix == "" ?
|
|
|
|
<Breadcrumb.Item active>{ this.props.bucket }</Breadcrumb.Item>
|
|
|
|
:
|
2022-02-28 20:31:26 +00:00
|
|
|
<LinkContainer key="BUCKET" to={ "/" + this.props.bucket + "/" }>
|
2022-02-28 16:59:06 +00:00
|
|
|
<Breadcrumb.Item>{ this.props.bucket }</Breadcrumb.Item>
|
|
|
|
</LinkContainer>
|
|
|
|
}
|
|
|
|
{ items }
|
|
|
|
</Breadcrumb>
|
2022-02-28 16:15:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
openInfo(f: string) {
|
|
|
|
this.setState({info: f, iInfo: this.state.iInfo + 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
openUpload() {
|
|
|
|
this.setState({iUpload: this.state.iUpload + 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
onUploadComplete() {
|
|
|
|
this.loadEntries();
|
|
|
|
}
|
2022-02-28 16:15:39 +00:00
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
render() {
|
2022-02-28 16:15:39 +00:00
|
|
|
if (!this.state.loaded) {
|
|
|
|
return (
|
2022-02-28 16:59:06 +00:00
|
|
|
<>
|
|
|
|
{ this.renderBreadcrumbs() }
|
|
|
|
<Alert variant="secondary">Loading...</Alert>
|
|
|
|
</>
|
2022-02-28 16:15:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
return (
|
2022-02-28 16:59:06 +00:00
|
|
|
<>
|
2022-02-28 20:31:26 +00:00
|
|
|
{ this.state.iUpload > 0 ?
|
|
|
|
<UploadFiles
|
|
|
|
key={ "upload" + this.state.iUpload }
|
|
|
|
client={this.props.client}
|
|
|
|
bucket={this.props.bucket}
|
|
|
|
prefix={this.props.prefix}
|
|
|
|
onUploadComplete={() => this.onUploadComplete()}
|
|
|
|
/>
|
|
|
|
: <></> }
|
|
|
|
{ this.state.info ?
|
|
|
|
<ObjectInfo
|
|
|
|
key={ "info" + this.state.iInfo }
|
|
|
|
client={ this.props.client }
|
|
|
|
bucket={this.props.bucket}
|
|
|
|
s3key={this.props.prefix + this.state.info}
|
|
|
|
filename={this.state.info} />
|
|
|
|
: <></> }
|
|
|
|
<Container className="pb-3">
|
|
|
|
<Stack direction="horizontal">
|
|
|
|
<div className="mt-1">
|
|
|
|
{ this.renderBreadcrumbs() }
|
|
|
|
</div>
|
|
|
|
<div className="ms-auto">
|
|
|
|
<Button size="sm" variant="info" onClick={(event) => this.openUpload()}>
|
|
|
|
Upload files
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Stack>
|
|
|
|
</Container>
|
2022-02-28 16:59:06 +00:00
|
|
|
<ListGroup>
|
|
|
|
{ this.state.folders.map((f) =>
|
2022-02-28 20:31:26 +00:00
|
|
|
<LinkContainer key={f + "/"} to={ "/" + this.props.bucket + "/" + this.props.prefix + f }>
|
|
|
|
<ListGroup.Item action>
|
|
|
|
<BsFolder /> { f }
|
2022-02-28 16:59:06 +00:00
|
|
|
</ListGroup.Item>
|
|
|
|
</LinkContainer>
|
|
|
|
)}
|
|
|
|
{ this.state.files.map((f) =>
|
2022-02-28 20:31:26 +00:00
|
|
|
<ListGroup.Item key={f} action onClick={() => this.openInfo(f)}>
|
|
|
|
<Stack direction="horizontal">
|
|
|
|
<div><BsFileEarmarkText /> { f }</div>
|
|
|
|
<div className="ms-auto">
|
|
|
|
<Button size="sm" variant="info" onClick={(event) => {
|
|
|
|
event.stopPropagation();
|
|
|
|
downloadFile(this.props.client, this.props.bucket, this.props.prefix + f, f);
|
|
|
|
}}>
|
|
|
|
<BsDownload />
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Stack>
|
2022-02-28 16:59:06 +00:00
|
|
|
</ListGroup.Item>
|
|
|
|
)}
|
|
|
|
</ListGroup>
|
|
|
|
</>
|
2022-02-28 16:08:49 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-28 20:31:26 +00:00
|
|
|
function isBlob(obj: any): obj is Blob {
|
|
|
|
return !! obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-28 16:08:49 +00:00
|
|
|
interface IClient {
|
|
|
|
client: S3Client;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const ObjectList1 = ({ client }: IClient) => {
|
|
|
|
const params = useParams();
|
|
|
|
const bucket = params["bucket"]!;
|
|
|
|
return <>
|
|
|
|
<ObjectList client={client} bucket={bucket} prefix="" key={bucket} />
|
|
|
|
</>;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const ObjectList2 = ({ client }: IClient) => {
|
|
|
|
const params = useParams();
|
|
|
|
const bucket = params["bucket"]!;
|
|
|
|
const prefix = params["*"] || "";
|
|
|
|
const key = bucket + "/" + prefix;
|
|
|
|
return <>
|
|
|
|
<ObjectList client={client} bucket={bucket} prefix={prefix} key={key} />
|
|
|
|
</>;
|
|
|
|
};
|