diff --git a/models/user_s_as.go b/models/user_s_as.go
new file mode 100644
index 000000000..56f4fc370
--- /dev/null
+++ b/models/user_s_as.go
@@ -0,0 +1,73 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// This file is part of MinIO Console Server
+// Copyright (c) 2022 MinIO, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// UserSAs user s as
+//
+// swagger:model userSAs
+type UserSAs struct {
+
+ // path
+ Path string `json:"path,omitempty"`
+
+ // recursive
+ Recursive bool `json:"recursive,omitempty"`
+
+ // version ID
+ VersionID string `json:"versionID,omitempty"`
+}
+
+// Validate validates this user s as
+func (m *UserSAs) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this user s as based on context it is used
+func (m *UserSAs) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *UserSAs) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *UserSAs) UnmarshalBinary(b []byte) error {
+ var res UserSAs
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/portal-ui/src/common/utils.ts b/portal-ui/src/common/utils.ts
index b57d52912..39fa40113 100644
--- a/portal-ui/src/common/utils.ts
+++ b/portal-ui/src/common/utils.ts
@@ -709,3 +709,13 @@ export const capacityColors = (usedSpace: number, maxSpace: number) => {
return "#07193E";
};
+
+export const getClientOS = (): string => {
+ const getPlatform = get(window.navigator, "platform", "undefined");
+
+ if (!getPlatform) {
+ return "undefined";
+ }
+
+ return getPlatform;
+};
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
index 5bd36dd78..1d7a065fc 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjects.tsx
@@ -767,6 +767,7 @@ const ListObjects = () => {
encodeURLString(object.name),
object.version_id,
object.size,
+ null,
(progress) => {
dispatch(
updateProgress({
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
index 9a2ca0d44..9f711d219 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ObjectDetailPanel.tsx
@@ -35,6 +35,7 @@ import { ErrorResponseHandler } from "../../../../../../common/types";
import {
decodeURLString,
encodeURLString,
+ getClientOS,
niceBytes,
niceBytesInt,
niceDaysInt,
@@ -87,6 +88,7 @@ import {
setVersionsModeEnabled,
updateProgress,
} from "../../../../ObjectBrowser/objectBrowserSlice";
+import RenameLongFileName from "../../../../ObjectBrowser/RenameLongFilename";
const styles = () =>
createStyles({
@@ -155,7 +157,6 @@ const ObjectDetailPanel = ({
bucketName,
versioning,
locking,
-
onClosePanel,
}: IObjectDetailPanelProps) => {
const dispatch = useAppDispatch();
@@ -183,6 +184,7 @@ const ObjectDetailPanel = ({
const [deleteOpen, setDeleteOpen] = useState(false);
const [previewOpen, setPreviewOpen] = useState(false);
const [totalVersionsSize, setTotalVersionsSize] = useState(0);
+ const [longFileOpen, setLongFileOpen] = useState(false);
const internalPathsDecoded = decodeURLString(internalPaths) || "";
const allPathData = internalPathsDecoded.split("/");
@@ -282,16 +284,29 @@ const ObjectDetailPanel = ({
setShareFileModalOpen(false);
};
+ const closeFileOpen = () => {
+ setLongFileOpen(false);
+ };
+
const downloadObject = (object: IFileInfo) => {
const identityDownload = encodeURLString(
`${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`
);
+ if (
+ object.name.length > 200 &&
+ getClientOS().toLowerCase().includes("win")
+ ) {
+ setLongFileOpen(true);
+ return;
+ }
+
const downloadCall = download(
bucketName,
internalPaths,
object.version_id,
parseInt(object.size || "0"),
+ null,
(progress) => {
dispatch(
updateProgress({
@@ -577,6 +592,16 @@ const ObjectDetailPanel = ({
closeInspectModalAndRefresh={closeInspectModal}
/>
)}
+ {longFileOpen && actualInfo && (
+
+ )}
{loadingObjectInfo ? (
{loaderForContainer}
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
index c4c8f2173..d6ef32f91 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/VersionsNavigator.tsx
@@ -251,6 +251,7 @@ const VersionsNavigator = ({
internalPaths,
object.version_id,
parseInt(object.size || "0"),
+ null,
(progress) => {
dispatch(
updateProgress({
diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
index 6f0c463be..1a6ecc845 100644
--- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
+++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts
@@ -16,12 +16,14 @@
import { BucketObjectItem } from "./ListObjects/types";
import { IAllowResources } from "../../../types";
+import { encodeURLString } from "../../../../../common/utils";
export const download = (
bucketName: string,
objectPath: string,
versionID: any,
fileSize: number,
+ overrideFileName: string | null = null,
progressCallback: (progress: number) => void,
completeCallback: () => void,
errorCallback: () => void,
@@ -29,7 +31,11 @@ export const download = (
) => {
const anchor = document.createElement("a");
document.body.appendChild(anchor);
- let path = `/api/v1/buckets/${bucketName}/objects/download?prefix=${objectPath}`;
+ let path = `/api/v1/buckets/${bucketName}/objects/download?prefix=${objectPath}${
+ overrideFileName !== null && overrideFileName.trim() !== ""
+ ? `&override_file_name=${encodeURLString(overrideFileName || "")}`
+ : ""
+ }`;
if (versionID) {
path = path.concat(`&version_id=${versionID}`);
}
diff --git a/portal-ui/src/screens/Console/ObjectBrowser/RenameLongFilename.tsx b/portal-ui/src/screens/Console/ObjectBrowser/RenameLongFilename.tsx
new file mode 100644
index 000000000..c3ee7668b
--- /dev/null
+++ b/portal-ui/src/screens/Console/ObjectBrowser/RenameLongFilename.tsx
@@ -0,0 +1,185 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 MinIO, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+import React, { useState } from "react";
+import Grid from "@mui/material/Grid";
+import createStyles from "@mui/styles/createStyles";
+import { Button } from "@mui/material";
+import makeStyles from "@mui/styles/makeStyles";
+import { Theme } from "@mui/material/styles";
+import { EditIcon } from "../../../icons";
+import {
+ containerForHeader,
+ formFieldStyles,
+ modalStyleUtils,
+ spacingUtils,
+} from "../Common/FormComponents/common/styleLibrary";
+import { IFileInfo } from "../Buckets/ListBuckets/Objects/ObjectDetails/types";
+import { encodeURLString } from "../../../common/utils";
+import { download } from "../Buckets/ListBuckets/Objects/utils";
+import {
+ cancelObjectInList,
+ completeObject,
+ failObject,
+ setNewObject,
+ updateProgress,
+} from "./objectBrowserSlice";
+import { makeid, storeCallForObjectWithID } from "./transferManager";
+import { useAppDispatch } from "../../../store";
+import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
+import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+
+interface IRenameLongFilename {
+ open: boolean;
+ bucketName: string;
+ internalPaths: string;
+ currentItem: string;
+ actualInfo: IFileInfo;
+ closeModal: () => void;
+}
+
+const useStyles = makeStyles((theme: Theme) =>
+ createStyles({
+ ...modalStyleUtils,
+ ...formFieldStyles,
+ ...spacingUtils,
+ ...containerForHeader(theme.spacing(4)),
+ })
+);
+
+const RenameLongFileName = ({
+ open,
+ closeModal,
+ currentItem,
+ internalPaths,
+ actualInfo,
+ bucketName,
+}: IRenameLongFilename) => {
+ const classes = useStyles();
+ const dispatch = useAppDispatch();
+
+ const [newFileName, setNewFileName] = useState(currentItem);
+
+ const doDownload = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ const identityDownload = encodeURLString(
+ `${bucketName}-${
+ actualInfo.name
+ }-${new Date().getTime()}-${Math.random()}`
+ );
+
+ const downloadCall = download(
+ bucketName,
+ internalPaths,
+ actualInfo.version_id,
+ parseInt(actualInfo.size || "0"),
+ newFileName,
+ (progress) => {
+ dispatch(
+ updateProgress({
+ instanceID: identityDownload,
+ progress: progress,
+ })
+ );
+ },
+ () => {
+ dispatch(completeObject(identityDownload));
+ },
+ () => {
+ dispatch(failObject(identityDownload));
+ },
+ () => {
+ dispatch(cancelObjectInList(identityDownload));
+ }
+ );
+ const ID = makeid(8);
+ storeCallForObjectWithID(ID, downloadCall);
+ dispatch(
+ setNewObject({
+ ID,
+ bucketName,
+ done: false,
+ instanceID: identityDownload,
+ percentage: 0,
+ prefix: newFileName,
+ type: "download",
+ waitingForFile: true,
+ failed: false,
+ cancelled: false,
+ })
+ );
+
+ downloadCall.send();
+ closeModal();
+ };
+
+ return (
+ }
+ >
+
+ The file you are trying to download has a long name.
+
+ This can cause issues on Windows Systems by trimming the file name after
+ download.
+
+ Would you like to rename the file for this download?
+