From b45c73b36406ebb55021490804f0602fcb862d7c Mon Sep 17 00:00:00 2001 From: zm <1334717033@qq.com> Date: Tue, 12 Mar 2024 18:04:31 +0800 Subject: [PATCH] =?UTF-8?q?2024=E5=B9=B43=E6=9C=8812=E6=97=A518:04:19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 + .../controller/MinioUploadController.java | 231 +++++++++++++-- .../com/zhangmeng/tools/dto/BucketFile.java | 68 +++++ .../com/zhangmeng/tools/utils/MinioUtils.java | 264 +++++++++++++++++- src/main/resources/fxml/minio-upload.fxml | 45 +-- 5 files changed, 564 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/zhangmeng/tools/dto/BucketFile.java diff --git a/pom.xml b/pom.xml index 3cb0a6b..52be934 100644 --- a/pom.xml +++ b/pom.xml @@ -486,6 +486,12 @@ rsyntaxtextarea 3.3.4 + + + org.apache.tika + tika-core + 1.27 + diff --git a/src/main/java/com/zhangmeng/tools/controller/MinioUploadController.java b/src/main/java/com/zhangmeng/tools/controller/MinioUploadController.java index 2aacada..90039de 100644 --- a/src/main/java/com/zhangmeng/tools/controller/MinioUploadController.java +++ b/src/main/java/com/zhangmeng/tools/controller/MinioUploadController.java @@ -2,18 +2,23 @@ package com.zhangmeng.tools.controller; import cn.hutool.crypto.digest.DigestUtil; -import com.zhangmeng.tools.utils.AlertUtils; -import com.zhangmeng.tools.utils.EncryptUtils; -import com.zhangmeng.tools.utils.ImagePath; -import com.zhangmeng.tools.utils.MinioUtils; +import com.zhangmeng.tools.dto.BucketFile; +import com.zhangmeng.tools.utils.*; +import io.minio.messages.Bucket; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.TextField; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.stage.DirectoryChooser; -import javafx.stage.FileChooser; -import javafx.stage.Stage; +import javafx.scene.text.Text; +import javafx.stage.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; @@ -21,6 +26,10 @@ import org.apache.commons.io.FilenameUtils; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; /** * 大文件上传 @@ -41,9 +50,6 @@ public class MinioUploadController { @FXML public TextField secretKey; - @FXML - public TextField bucket; - @FXML public ImageView iv; @@ -56,9 +62,140 @@ public class MinioUploadController { @FXML public TextField file_path; + @FXML + public ComboBox backet_list; + + private final ObservableList backet_item_list = FXCollections.observableArrayList(); + private final ObservableList list = FXCollections.observableArrayList(); + + @FXML + public TableView tableview; + + @FXML + public TableColumn file_name; + + @FXML + public TableColumn file_size; + + @FXML + public TableColumn file_url; + + @FXML + public TableColumn file_bucket; + + @FXML + public Button reload_config; + + @FXML + public Button delete_bucket; + + @FXML + public Button create_bucket; + + @FXML + public TextField create_bucket_name; + + public void reload(String bucketName){ + if (bucketName == null){ + return; + } + List bucketFiles = MinioUtils.getFileList(bucketName); + if (bucketFiles.size()>0){ + list.clear(); + list.addAll(bucketFiles); + } + } + + + public void init_bucket_list(){ + backet_item_list.clear(); + //获取所有储存桶 + List buckets = MinioUtils.getAllBuckets(); + if (buckets.size()>0){ + for (Bucket bucket : buckets) { + backet_item_list.add(bucket.name()); + } + } + + } + + public void init_minio_config(){ + //默认值 + endpoint.setText("http://192.168.1.254:9000"); + accessKey.setText("minioadmin"); + secretKey.setText("minioadmin"); + MinioUtils.objectMap.put("endpoint",endpoint.getText()); + MinioUtils.objectMap.put("accessKey",accessKey.getText()); + MinioUtils.objectMap.put("secretKey",secretKey.getText()); + } + + public void reload_minio_config(){ + + } + @FXML public void initialize() { + init_minio_config(); + + this.backet_list.setItems(backet_item_list); + backet_list.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + if (newValue != null){ + reload(newValue); + } + } + }); + init_bucket_list(); + + tableview.setItems(list); + file_name.setCellValueFactory(new PropertyValueFactory("fileName")); + file_size.setCellValueFactory(new PropertyValueFactory("size")); + file_url.setCellValueFactory(new PropertyValueFactory("url")); + file_bucket.setCellValueFactory(new PropertyValueFactory("bucketName")); + + file_name.prefWidthProperty().bind(tableview.widthProperty().divide(tableview.getColumns().size())); + file_size.prefWidthProperty().bind(tableview.widthProperty().divide(tableview.getColumns().size())); + file_url.prefWidthProperty().bind(tableview.widthProperty().divide(tableview.getColumns().size())); + file_bucket.prefWidthProperty().bind(tableview.widthProperty().divide(tableview.getColumns().size())); + + // 创建上下文菜单 + final ContextMenu contextMenu = new ContextMenu(); + MenuItem cat = new MenuItem("查看"); + MenuItem copy_url = new MenuItem("复制url"); + MenuItem menuItem = new MenuItem("删除"); + menuItem.setOnAction(event -> { + list.remove(tableview.getSelectionModel().getSelectedItem()); + }); + contextMenu.getItems().add(cat); + cat.setOnAction(event -> { + BucketFile selectedItem = tableview.getSelectionModel().getSelectedItem(); + iv.setImage(new Image(selectedItem.getUrl())); + }); + contextMenu.getItems().add(copy_url); + copy_url.setOnAction(event -> { + ClipboardUtils.setString(tableview.getSelectionModel().getSelectedItem().getUrl()); + }); + + contextMenu.getItems().add(menuItem); + // 添加事件监听器来处理上下文菜单的显示 + tableview.setOnContextMenuRequested(event -> { + // 只有当表格有选中项时才显示上下文菜单 + if (!tableview.getSelectionModel().isEmpty()) { + contextMenu.show(tableview, event.getScreenX(), event.getScreenY()); + } + }); + + tableview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, BucketFile oldValue, BucketFile newValue) { + if (newValue != null) { + System.out.println(newValue.getFileName()); + } + } + }); + file_choose.setText(null); ImageView iv = new ImageView(new Image(ImagePath.path(ImagePath.ImagePathType.IMAGE_FILE))); iv.setPreserveRatio(true); @@ -66,11 +203,6 @@ public class MinioUploadController { file_choose.setGraphic(iv); file_choose.setOnAction(event -> choose_file()); - //默认值 - endpoint.setText("http://192.168.1.14:9000"); - accessKey.setText("minioadmin"); - secretKey.setText("minioadmin"); - bucket.setText("mediafiles"); upload.setOnAction(event -> { @@ -89,19 +221,62 @@ public class MinioUploadController { return; } - if (bucket.getText().length() == 0) { - AlertUtils.alert_warning("bucket不能为空!"); + if (backet_list.getSelectionModel().getSelectedItem() == null) { + AlertUtils.alert_warning("请选择bucket再试!"); return; } File file = new File(file_path.getText()); - String extension = FilenameUtils.getExtension(file.getName()); - if (extension.equals("jpg") || extension.equals("png")) { - images_upload(); +// String extension = FilenameUtils.getExtension(file.getName()); +// if (extension.equals("jpg") || extension.equals("png")) { +// images_upload(); +// } +// if (extension.equals("mp4") || extension.equals("avi")) { +// videos_upload(); +// } + + MinioUtils.upload_file(file,backet_list.getSelectionModel().getSelectedItem()); + reload(backet_list.getSelectionModel().getSelectedItem()); + }); + + reload_config.setOnAction(event -> { + + }); + + create_bucket.setOnAction(event -> { + if (create_bucket_name.getText().length() == 0) { + AlertUtils.alert_warning("bucket的名称不能为空!"); + return; } - if (extension.equals("mp4") || extension.equals("avi")) { - videos_upload(); + MinioUtils.createBucket(create_bucket_name.getText()); + init_bucket_list(); + }); + delete_bucket.setOnAction(event -> { + + // 用户点击了确定按钮 + if (backet_list.getSelectionModel().getSelectedItem() == null){ + MinioUtils.warning_msg("请选择将要删除的bucket在试!"); + return; } + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.initModality(Modality.APPLICATION_MODAL); + alert.setTitle("确认"); + alert.setHeaderText("你确定要继续吗?"); + alert.setContentText("这将执行一个操作。"); + Optional result = alert.showAndWait(); + result.ifPresent(response -> { + if (response == ButtonType.OK) { + //清空 + List fileList = MinioUtils.getFileList(backet_list.getSelectionModel().getSelectedItem()); + List del_list = fileList.stream().map(BucketFile::getFileName).collect(Collectors.toList()); + + MinioUtils.removeFiles(backet_list.getSelectionModel().getSelectedItem(), del_list); + MinioUtils.removeBucket(backet_list.getSelectionModel().getSelectedItem()); + AlertUtils.alert_msg("删除成功!"); + init_bucket_list(); + } else { + } + }); }); } @@ -116,14 +291,14 @@ public class MinioUploadController { // process the chunk String fileMd5 = DigestUtil.md5Hex(chunk); //判断分块是否存在 - if (!MinioUtils.checkChunk(fileMd5, i, bucket.getText())) { + if (!MinioUtils.checkChunk(fileMd5, i, backet_list.getSelectionModel().getSelectedItem())) { //得到分块文件的目录路径 String chunkFileFolderPath = MinioUtils.getChunkFileFolderPath(fileMd5); //得到分块文件的路径 String chunkFilePath = chunkFileFolderPath + i; try { //将文件存储至minIO - MinioUtils.addMediaFilesToMinIO(chunk, bucket.getText(), chunkFilePath, "application/octet-stream"); + MinioUtils.addMediaFilesToMinIO(chunk, backet_list.getSelectionModel().getSelectedItem(), chunkFilePath, "application/octet-stream"); } catch (Exception ex) { ex.printStackTrace(); } @@ -136,7 +311,7 @@ public class MinioUploadController { try { //上传文件到minIO - MinioUtils.addMediaFilesToMinIO(filereader.getFile().getAbsolutePath(), bucket.getText(), mergeFilePath); + MinioUtils.addMediaFilesToMinIO(filereader.getFile().getAbsolutePath(), backet_list.getSelectionModel().getSelectedItem(), mergeFilePath); log.debug("合并文件上传MinIO完成{}", filereader.getFile().getAbsolutePath()); } catch (Exception e) { e.printStackTrace(); @@ -156,7 +331,7 @@ public class MinioUploadController { String md5Hex = DigestUtil.md5Hex(bytes); String contentType = Files.probeContentType(file.toPath()); String extension = FilenameUtils.getExtension(file.getName()); - MinioUtils.addMediaFilesToMinIO(bytes, bucket.getText(), md5Hex + "." + extension, contentType); + MinioUtils.addMediaFilesToMinIO(bytes, backet_list.getSelectionModel().getSelectedItem(), md5Hex + "." + extension, contentType); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/zhangmeng/tools/dto/BucketFile.java b/src/main/java/com/zhangmeng/tools/dto/BucketFile.java new file mode 100644 index 0000000..b0e53fd --- /dev/null +++ b/src/main/java/com/zhangmeng/tools/dto/BucketFile.java @@ -0,0 +1,68 @@ +package com.zhangmeng.tools.dto; + + +/** + * @author zhangmeng + * @version 1.0 + * @date 2024-03-07 16:31 + */ + +public class BucketFile { + + private String bucketName; + private String fileName; + private boolean isDir; + private Long size; + private String url; + + public BucketFile() { + } + + public BucketFile(String bucketName, String fileName, boolean isDir, Long size, String url) { + this.bucketName = bucketName; + this.fileName = fileName; + this.isDir = isDir; + this.size = size; + this.url = url; + } + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public boolean isDir() { + return isDir; + } + + public void setDir(boolean dir) { + isDir = dir; + } + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/main/java/com/zhangmeng/tools/utils/MinioUtils.java b/src/main/java/com/zhangmeng/tools/utils/MinioUtils.java index b8cf44a..d62f055 100644 --- a/src/main/java/com/zhangmeng/tools/utils/MinioUtils.java +++ b/src/main/java/com/zhangmeng/tools/utils/MinioUtils.java @@ -1,19 +1,30 @@ package com.zhangmeng.tools.utils; -import io.minio.GetObjectArgs; -import io.minio.MinioClient; -import io.minio.PutObjectArgs; -import io.minio.UploadObjectArgs; +import com.zhangmeng.tools.dto.BucketFile; +import io.minio.*; +import io.minio.errors.*; +import io.minio.http.Method; +import io.minio.messages.Bucket; +import io.minio.messages.DeleteObject; +import io.minio.messages.Item; +import javafx.application.Platform; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.tika.Tika; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.stream.Collectors; @Slf4j public class MinioUtils { @@ -25,6 +36,35 @@ public class MinioUtils { } } + public static Map objectMap = new HashMap<>(); + public static MinioClient getDefault() { + if (minioClient == null) { + Object endpoint = objectMap.get("endpoint"); + Object accessKey = objectMap.get("accessKey"); + Object secretKey = objectMap.get("secretKey"); + if (endpoint == null ){ + Platform.runLater(()->{ + AlertUtils.alert_warning("配置文件:endpoint 为空"); + }); + return null; + } + if (accessKey == null ){ + Platform.runLater(()->{ + AlertUtils.alert_warning("配置文件:accessKey 为空"); + }); + return null; + } + + if (secretKey == null ){ + Platform.runLater(()->{ + AlertUtils.alert_warning("配置文件:secretKey 为空"); + }); + return null; + } + minioClient = MinioClient.builder().endpoint(endpoint.toString()).credentials(accessKey.toString(), secretKey.toString()).build(); + } + return minioClient; + } public static String getFilePathByMd5(String fileMd5, String fileExt) { return fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + fileExt; @@ -56,6 +96,66 @@ public class MinioUtils { return false; } + public static List getAllObjectsByPrefix(String bucketName, + String prefix, + boolean recursive) { + List list = new ArrayList<>(); + Iterable> objectsIterator = getDefault().listObjects( + ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build()); + if (objectsIterator != null) { + for (Result o : objectsIterator) { + Item item = null; + try { + item = o.get(); + } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) { + e.printStackTrace(); + } + list.add(item); + } + } + return list; + } + + public static List getFileList(String bucketName) { + + List itemList = getAllObjectsByPrefix(bucketName, null, true); + List list = new ArrayList<>(); + if (itemList.size() > 0 ){ + list = itemList.stream().map(i -> { + BucketFile bucketFile = new BucketFile(); + bucketFile.setBucketName(bucketName); + bucketFile.setDir(i.isDir()); + bucketFile.setFileName(i.objectName()); + bucketFile.setSize(i.size()); + //获取url + String presignedObjectUrl = getPresignedObjectUrl(bucketName, i.objectName()); + bucketFile.setUrl(presignedObjectUrl); + return bucketFile; + }).collect(Collectors.toList()); + } + return list; + } + + /** + * 获得文件外链 + * + * @param bucketName + * @param objectName + * @return url + */ + public static String getPresignedObjectUrl(String bucketName, String objectName) { + GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() + .bucket(bucketName) + .object(objectName) + .method(Method.GET).build(); + try { + return getDefault().getPresignedObjectUrl(args); + } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException | ServerException e) { + e.printStackTrace(); + } + return null; + } + public static class FileChunkReader implements AutoCloseable { private final FileChannel channel; @@ -293,7 +393,159 @@ public class MinioUtils { } - public static void main(String[] args) { - test("G:\\FXGL基础入门教程 Java游戏引擎教程_1简介与游戏演示\\1-1 简介与游戏演示-1080P 高清-AVC.mp4"); + /** + * 获得所有Bucket列表 + * + * @return + */ + public static List getAllBuckets() { + try { + return getDefault().listBuckets(); + } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) { + e.printStackTrace(); + } + return null; + } + + public static void upload_file(File file,String bucketName){ + //文件名 + String fileName = file.getName(); + String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(fileName, "."); + String contentType = getContentType(file); + if (bucketName == null){ + AlertUtils.alert_warning("上传的bucket不能为空!"); + return; + } + uploadFile(bucketName, file, newFileName, contentType); + AlertUtils.alert_msg("文件上传成功"); + } + + /** + * 使用MultipartFile进行文件上传 + * + * @param bucketName 存储桶 + * @param file 文件名 + * @param objectName 对象名 + * @param contentType 类型 + * @return + */ + public static ObjectWriteResponse uploadFile(String bucketName, File file, String objectName, String contentType) { + FileInputStream inputStream = null; + try { + inputStream = new FileInputStream(file); + return minioClient.putObject( + PutObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .contentType(contentType) + .stream(inputStream,inputStream.available(), -1) + .build()); + } catch (IOException | ServerException | InsufficientDataException | XmlParserException | InternalException | InvalidResponseException | InvalidKeyException | NoSuchAlgorithmException | ErrorResponseException e) { + e.printStackTrace(); + } finally { + if (inputStream != null){ + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + + + public static String getContentType(File file){ + Tika tika = new Tika(); + try { + return tika.detect(file); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 启动SpringBoot容器的时候初始化Bucket + * 如果没有Bucket则创建 + * + * @param bucketName + */ + @SneakyThrows(Exception.class) + public static void createBucket(String bucketName) { + if (!bucketExists(bucketName)) { + getDefault().makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + success_msg(bucketName + "创建成功!"); + }else { + warning_msg(bucketName + "已存在"); + } + } + + public static void warning_msg(String msg){ + Platform.runLater(()->{ + AlertUtils.alert_warning(msg ); + }); + } + + public static void success_msg(String msg){ + Platform.runLater(()->{ + AlertUtils.alert_msg(msg); + }); + } + + /** + * 判断Bucket是否存在,true:存在,false:不存在 + * + * @param bucketName + * @return + */ + @SneakyThrows(Exception.class) + public static boolean bucketExists(String bucketName) { + return getDefault().bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } + + /** + * 根据bucketName删除Bucket,true:删除成功; false:删除失败,文件或已不存在 + * + * @param bucketName + * @throws Exception + */ + @SneakyThrows(Exception.class) + public static void removeBucket(String bucketName) { + getDefault().removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } + + /** + * 删除文件 + * + * @param bucketName 存储桶 + * @param objectName 文件名称 + */ + @SneakyThrows(Exception.class) + public static void removeFile(String bucketName, String objectName) { + minioClient.removeObject( + RemoveObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .build()); + } + + /** + * 批量删除文件 + * + * @param bucketName 存储桶 + * @param keys 需要删除的文件列表 + * @return + */ + public static void removeFiles(String bucketName, List keys) { + List objects = new LinkedList<>(); + keys.forEach(s -> { + objects.add(new DeleteObject(s)); + try { + removeFile(bucketName, s); + } catch (Exception e) { + log.error("[Minio工具类]>>>> 批量删除文件,异常:", e); + } + }); } } diff --git a/src/main/resources/fxml/minio-upload.fxml b/src/main/resources/fxml/minio-upload.fxml index 328f1bb..d954577 100644 --- a/src/main/resources/fxml/minio-upload.fxml +++ b/src/main/resources/fxml/minio-upload.fxml @@ -1,32 +1,45 @@ + - + + -