diff --git a/Core/Crypto.java b/Core/Crypto.java index 88c2f6c..9d48f66 100644 --- a/Core/Crypto.java +++ b/Core/Crypto.java @@ -5,6 +5,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * 加解密类 + */ public class Crypto { private String filePath; // 文件路径 private int depth = 5; // 加密深度(指对每个字节进行多少次操作) @@ -12,16 +15,22 @@ public class Crypto { private String encryptedSuffix; // 加上crypt后的后缀名 private String sourceSuffix; // 原始后缀名 private boolean isEncrypt = true; // 当前要进行的是什么操作,true为加密,false为解密 - // 用于进度显示 private double fileLength; // 文件总字节数 private double currentProgress; // 当前已处理的字节数 + + public double getFileLength() { + return fileLength; + } + public double getCurrentProgress() { + return currentProgress; + } + /** * 导入参数:filePath:String *

导入后立刻进行处理
* 1、文件是否存在
* 2、提取后缀名

- * * @param _filePath 需要加解密的文件路径 */ private void filePath(String _filePath) { @@ -72,26 +81,30 @@ public float ReturnProgress() { return (float) (currentProgress / fileLength); } + + /** * Crypto类主要函数,传入参数并开始加解密操作 - * * @param _filePath 文件路径 * @param keyStr 作为密钥的字符串 + * @throws Exception 可能有Null,FileNotFound */ public void Crypt(String _filePath, String keyStr) throws Exception{ - // 录入参数 + // 录入参数并进行相关处理 filePath(_filePath); key(keyStr); // File sourceFile = new File(filePath); + if(!sourceFile.exists()) + throw new NullPointerException(); File outputFile = null; - // 确认输出文件的文件名 + // 确认输出文件的后缀名 if (isEncrypt) { outputFile = new File(filePath.replace(sourceSuffix, "crypt" + sourceSuffix)); } else { outputFile = new File(filePath.replace(encryptedSuffix, sourceSuffix)); } - // + // 创建各种流 InputStream in = null; OutputStream out = null; if (!outputFile.exists()) diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index f3e3c21..f98b469 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ Manifest-Version: 1.0 -Main-Class: UI.Main +Main-Class: UI.MainUI diff --git a/UI/AboutWindow.java b/UI/AboutWindow.java new file mode 100644 index 0000000..af6b419 --- /dev/null +++ b/UI/AboutWindow.java @@ -0,0 +1,59 @@ +package UI; + +import javax.swing.*; +import java.awt.*; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class AboutWindow extends JFrame{ + AboutWindow(){ + Font font = new Font("Microsoft YaHei", Font.PLAIN, 12); + // 读取About.txt内的说明信息 + String strAbout = ""; + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("Resource/About.txt"), StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String tmp = ""; + while ((tmp = reader.readLine()) != null) { + sb.append(tmp + "\n"); + } + strAbout = sb.toString(); + reader.close(); + } catch (FileNotFoundException ex) { + ex.printStackTrace(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } + // 创建窗口 + setTitle("关于"); + JPanel panel = new JPanel(new BorderLayout()); + JTextPane text = new JTextPane(); + JScrollPane scrollPane = new JScrollPane(text); + panel.add(scrollPane, BorderLayout.CENTER); + text.setBackground(new Color(70, 70, 70)); + text.setForeground(new Color(139, 150, 158)); + text.setFont(font); + text.setEditable(false); + // 逐字输出关于信息 + String finalStrAbout = strAbout; + new Thread(() -> { + for (int i = 0; i < finalStrAbout.length(); i++) { + try { + Thread.sleep(100); + text.setText(text.getText() + finalStrAbout.charAt(i)); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + }).start(); + + setContentPane(panel); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setBounds(100, 100, 420, 500); + setVisible(true); + } + +} diff --git a/UI/FX.java b/UI/FX.java new file mode 100644 index 0000000..4392b6d --- /dev/null +++ b/UI/FX.java @@ -0,0 +1,309 @@ +package UI; + +import Core.Crypto; + +import javax.swing.*; +import java.awt.*; +import java.util.Random; + +/** + * 动画效果窗口 + */ +public class FX extends JFrame { + private JPanel mainPanel, minorPanel; // 主Panel 和 副Panel + private JLabel lblStatus; // 表示状态的Label + private JTextField[][] textNodes; // 每个格子都是一个TextField,这是个TextFiled数组 + private Crypto crypto; // 存放Crypto的实例 + private int timeRatio = 50; // 决定动画持续时间的系数,50约为12s + + // 预先定义防止反复创建变量,优化性能 + private Color colorRed = new Color(150,32,32); // 红色 + private int redWaveRange = 80; // 红色颜色波动范围 + private Color colorGreen = new Color(54, 150, 48); // 绿色 + private int greenWaveRange = 80; // 绿色颜色波动范围 + + /** + * FX的主函数,仅供单独打开动画调试用
+ * @param args 命令行参数 + */ + public static void main(String[] args){ + // 若需仅打开动画,便启动这个主函数,并设置上面的timeRatio以控制持续时间 + new FX(null); + } + + /** + * 从时间秒数到timeRatio系数的转换函数
+ * 利用函数拟合器获得,基本为线性,相关度0.99996
+ * @param secs 秒数 + * @return 返回timeRatio系数 + */ + private int secsToRatio(float secs){ + if(secs<5) + return 1; + + double A,B,C,D,X=secs; + A = 1.01563088882032E+15; + B = -1.00541928494497; + C = 176451864312395d; + D = -20.3014929981133; + return (int)((A-D)/(1+Math.pow(X/C,B))+D); + } + + /** + * 构造函数
+ * 导入Crypto实例后 判断是否为单独运行动画
+ * 不是:运行{@link FX#SpeedTest()}函数 ({@link FX#SpeedTest()}之后会自动调用下面两个函数)
+ * 是:跳过{@link FX#SpeedTest()}函数,直接运行{@link FX#Initialize()}函数和{@link FX#Start()}函数
+ * @param _crypto 即将运行的{@link Core.Crypto}实例 + */ + FX(Crypto _crypto) { + crypto = _crypto; + if(crypto==null){ + Initialize(); + Start(); + } + else if(crypto.getFileLength()<10000) { + timeRatio = 1; + Initialize(); + Start(); + } + else{ + SpeedTest(); + } + + } + + /** + * 初始化界面元素
+ */ + private void Initialize() { + // 基本布局:JFrame(mainPanel:Border(North->minorPanel:Grid(textField[15][8]))) + mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBackground(new Color(36, 36, 36)); + minorPanel = new JPanel(new GridLayout(15, 8,1,1)); + minorPanel.setBackground(new Color(36, 36, 36)); + lblStatus = new JLabel("File Encrypting..."); + lblStatus.setForeground(new Color(139, 150, 158)); + mainPanel.add(lblStatus, BorderLayout.NORTH); + mainPanel.add(minorPanel, BorderLayout.CENTER); + // 初始化textNodes数组 + textNodes = new JTextField[15][8]; + Font font = new Font("微软雅黑",Font.BOLD,12); + Color colorRed = new Color(150,32,32); + Color colorBg = new Color(180,180,180); + TextBorderUtlis border = new TextBorderUtlis(new Color(36, 36, 36), 0, false); + // 设置每一个格子 + for (int row = 0; row < 15; row++) { + for (int col = 0; col < 8; col++) { + JTextField textField = textNodes[row][col]; + textNodes[row][col] = new JTextField("255", 3); + textNodes[row][col].setForeground(colorBg); + textNodes[row][col].setBackground(colorRed); + textNodes[row][col].setHorizontalAlignment(JTextField.CENTER); + textNodes[row][col].setBorder(border); + textNodes[row][col].setVisible(false); + textNodes[row][col].setEditable(false); + textNodes[row][col].setFont(font); + minorPanel.add(textNodes[row][col]); + } + } + setContentPane(mainPanel); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setVisible(true); + setBounds(50,50,300,300); + } + + /** + * 测试当前系统加解密速度,以确定动画持续市时长
+ */ + private void SpeedTest() { + // 新的线程,用于测速:用时1s + new Thread(() -> { + long start, end; + float cryStart, cryEnd, speed = 0; + for (int i = 0; i < 10; i++) { + start = System.currentTimeMillis(); + cryStart = (int) crypto.getCurrentProgress(); + // 等待 + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // + end = System.currentTimeMillis(); + cryEnd = (int) crypto.getCurrentProgress(); + speed += (int) ((cryEnd - cryStart) / (end - start)); + } + // 测试完后调用 + SpeedTestComplete((int) speed / 10); + }).start(); + } + + /** + * 测速完成后调用的函数
+ * 在此处计算动画持续时间
+ * 以及调用{@link FX#Initialize()}和{@link FX#Start()}
+ * @param speed 前面测速所得的速度,单位bytes/ms
+ */ + private void SpeedTestComplete(int speed) { + System.out.println(speed); + int secs = (int) (crypto.getFileLength()/(speed*1000)); + System.out.printf("需要%d秒\n",secs); + timeRatio=secsToRatio(secs); + Initialize(); + Start(); + } + + /** + * 开始动画
+ */ + private void Start() { + // counts二维数组用于给每一个格子确定变化次数 + int counts[][] = new int[15][8]; + Random rd = new Random(); + // 填充数据以控制时间 + for (int row = 0; row < 15; row++) { + for (int col = 0; col < 8; col++) { + counts[row][col] = rd.nextInt(timeRatio) + 4;// 加4防止动画过短造成视觉效果不佳 + } + } + // 开始时格子逐渐显现的过程(逐个设置可见;颜色亮度波动) + // 遍历每一个格子并对齐调用nodeActionStart()使颜色变亮一会 + new Thread(() -> { + for (int row = 0; row < 15; row++) { + for (int col = 0; col < 8; col++) { + try { + Thread.sleep(5); + nodeActionStart(textNodes[row][col]); + textNodes[row][col].setVisible(true); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }).start(); + // 格子数字变换的效果 + // 遍历每一个格子,每遍历一次就将其剩余变换次数减1,并随机变换数字或不变 + new Thread(() -> { + while (true) { + boolean isComplete = true; + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + for (int row = 0; row < 15; row++) { + for (int col = 0; col < 8; col++) { + if (counts[row][col] > 0) { // 还未完成 + // 随机变或不变 + if(rd.nextInt(2)>0) + textNodes[row][col].setText((rd.nextInt(254) + 1) + ""); + counts[row][col]--; + isComplete = false; + } else if (counts[row][col] == 0) { // 已完成 + // 完成的格子就变成绿色,字改为0,然后调用nodeActionComplete()使颜色变亮一会 + counts[row][col]--; + textNodes[row][col].setText("0"); + textNodes[row][col].setBackground(colorGreen); + nodeActionComplete(textNodes[row][col]); + } + } + } + // 如果有任意一个格子还没完成就不会执行 + if (isComplete) { + // 全部完成时,遍历每一个格子并调用nodeActionComplete()使颜色变亮一会 + new Thread(() -> { + for (int row = 0; row < 15; row++) { + for (int col = 0; col < 8; col++) { + try { + Thread.sleep(5); + nodeActionComplete(textNodes[row][col]); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + // 等待1s后调用frameActionShrink()关闭动画 + new Thread(()->{ + try { + Thread.sleep(1000); + frameActionShrink(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + }).start(); + lblStatus.setText("File Encrypted"); + break; + } + } + }).start(); + } + + /** + * 格子开始时的动作 (红色:亮度上升再回落)
+ * @param text TextFiled格子实例 + */ + private void nodeActionStart(JTextField text) { + new Thread(() -> { + for (int i = 0; i < 80; i += 8) { + try { + Thread.sleep(20); + text.setBackground(new Color(150 + i, 32, 32)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + for (int i = 0; i < 80; i += 8) { + try { + Thread.sleep(20); + text.setBackground(new Color(230 - i, 32, 32)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } + /** + * 格子完成时的动作 (绿色:亮度上升再回落)
+ * @param text TextFiled格子实例 + */ + private void nodeActionComplete(JTextField text) { + new Thread(() -> { + for (int i = 0; i < 80; i += 8) { + try { + Thread.sleep(20); + text.setBackground(new Color(54, 150 + i, 48)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + for (int i = 0; i < 80; i += 8) { + try { + Thread.sleep(20); + text.setBackground(new Color(54, 230 - i, 48)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } + /** + * 动画完成时的动作 (窗口向上收缩并最后关闭)
+ */ + private void frameActionShrink(){ + new Thread(() -> { + for (int i = 0; i < getHeight(); i ++) { + try { + Thread.sleep(10); + setSize(getWidth(),getHeight()-(int)Math.sqrt(i)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + dispose(); + }).start(); + } + +} diff --git a/UI/Main.java b/UI/Main.java deleted file mode 100644 index 989d99d..0000000 --- a/UI/Main.java +++ /dev/null @@ -1,12 +0,0 @@ -package UI; - -import UI.MainUI; - -import java.io.IOException; - -public class Main { - public static void main(String args[]){ - MainUI ui = new MainUI(); - } - -} diff --git a/UI/MainUI.form b/UI/MainUI.form index ea9503d..7875f22 100644 --- a/UI/MainUI.form +++ b/UI/MainUI.form @@ -17,6 +17,7 @@ + diff --git a/UI/MainUI.java b/UI/MainUI.java index ac8c343..8459009 100644 --- a/UI/MainUI.java +++ b/UI/MainUI.java @@ -14,8 +14,7 @@ import java.awt.datatransfer.Transferable; import java.awt.event.*; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.*; @@ -30,14 +29,16 @@ public class MainUI extends JFrame { private JTextField textKey; // 密钥文本框 private JProgressBar progressBar; // 进度条 - private final Crypto crypto = new Crypto(); // Crypto类的唯一实例 + private Crypto crypto = new Crypto(); // Crypto类的唯一实例 + + public static void main(String[] args) { + new MainUI(); + } /** - * MainUI构造函数,目前主要负责事件添加和设置一些初始值 + * 用于分散构造函数:给窗口添加菜单栏(包含一个关于按钮) */ - public MainUI() { - // Swing GUI编辑器生成代码 - $$$setupUI$$$(); + private void addAboutMenu() { // 添加一个顶部菜单栏 Font font = new Font("Microsoft YaHei", Font.PLAIN, 12); Color fontColor = new Color(139, 150, 158); @@ -63,50 +64,34 @@ public MainUI() { menuBar.setBackground(backColor); menuBar.setBorder(border); setJMenuBar(menuBar); - // 设置菜单栏各事件 + // 设置菜单栏”关于“事件 menuItemAbout.addActionListener(e -> { - // 读取About.txt内的说明信息 - String strAbout = ""; - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("Resource/About.txt"))); - StringBuilder sb = new StringBuilder(); - String tmp = ""; - while ((tmp = reader.readLine()) != null) { - sb.append(tmp + "\n"); - } - strAbout = sb.toString(); - reader.close(); - } catch (FileNotFoundException ex) { - ex.printStackTrace(); - } catch (IOException ioException) { - ioException.printStackTrace(); - } - // 创建窗口 - JFrame frame = new JFrame("关于"); - JPanel panel = new JPanel(new BorderLayout()); - JTextPane text = new JTextPane(); - JScrollPane scrollPane = new JScrollPane(text); - panel.add(scrollPane, BorderLayout.CENTER); - text.setBackground(new Color(70, 70, 70)); - text.setForeground(new Color(139, 150, 158)); - text.setFont(font); - text.setEditable(false); - String finalStrAbout = strAbout; - new Thread(() -> { - for (int i = 0; i < finalStrAbout.length(); i++) { - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - text.setText(text.getText() + finalStrAbout.charAt(i)); - } - }).start(); - - frame.setContentPane(panel); - frame.setBounds(100, 100, 420, 500); - frame.setVisible(true); + new AboutWindow(); }); + } + + /** + * 用于分散构造函数:设置组件边框 + */ + private void setBorder() { + LineBorder border2 = new TextBorderUtlis(new Color(70, 70, 70), 2, false); + textFilePath.setBorder(border2); + textKey.setBorder(border2); + btnGenerateRandomKey.setBorder(border2); + progressBar.setBorder(border2); + btnEncryptDecrypt.setBorder(new TextBorderUtlis(new Color(70, 70, 70), 5, false)); + } + + /** + * MainUI构造函数,目前主要负责事件添加和设置一些初始值 + */ + public MainUI() { + // Swing GUI编辑器生成代码 + $$$setupUI$$$(); + // 添加菜单栏 + addAboutMenu(); + // 设置进度条最大值 + progressBar.setMaximum(100); // 拖入文件的事件 textFilePath.setTransferHandler(new TransferHandler() { @Override @@ -142,8 +127,6 @@ public boolean canImport(JComponent comp, DataFlavor[] flavors) { return false; } }); - // 设置进度条最大值 - progressBar.setMaximum(100); // 按钮“加密 / 解密”的点击事件 btnEncryptDecrypt.addActionListener(e -> { btnEncryptDecrypt.setEnabled(false); @@ -153,8 +136,7 @@ public boolean canImport(JComponent comp, DataFlavor[] flavors) { // 没写就提示 if (lblStatus.getText().length() == 20) { btnEncryptDecrypt.setEnabled(true); - } - else if (lblStatus.getText().startsWith("请先输入文件路径和密钥")) { + } else if (lblStatus.getText().startsWith("请先输入文件路径和密钥")) { btnEncryptDecrypt.setEnabled(true); lblStatus.setText(lblStatus.getText().replace("。", "") + "!"); } else { @@ -163,7 +145,8 @@ else if (lblStatus.getText().startsWith("请先输入文件路径和密钥")) { } } else { // 写了就开始运行加解密 - new BackRunnerCrypt().start(); + new FX(crypto); + new BW_Crypt().start(); } lblStatus.requestFocus(); }); @@ -198,18 +181,13 @@ public void focusLost(FocusEvent e) { SecureRandom rd = new SecureRandom(); String myAscii = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; StringBuilder keyStr = new StringBuilder(); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 32; i++) { keyStr.append(myAscii.charAt(rd.nextInt(62))); } textKey.setText(keyStr.toString()); }); // 边框设置 - LineBorder border2 = new TextBorderUtlis(new Color(70, 70, 70), 2, false); - textFilePath.setBorder(border2); - textKey.setBorder(border2); - btnGenerateRandomKey.setBorder(border2); - progressBar.setBorder(border2); - btnEncryptDecrypt.setBorder(new TextBorderUtlis(new Color(70, 70, 70), 5, false)); + setBorder(); // 修改窗口风格为当前系统对应的风格 try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -220,8 +198,9 @@ public void focusLost(FocusEvent e) { setContentPane(mainPanel); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(true); - setTitle("Crypto v0.1.3 by LZHJK"); + setTitle("Crypto"); setVisible(true); + setLocation(300, 100); pack(); // 设置焦点位置 textKey.requestFocus(); @@ -230,7 +209,7 @@ public void focusLost(FocusEvent e) { /** * 用于后台运行加解密 */ - class BackRunnerCrypt implements Runnable { + class BW_Crypt implements Runnable { public void start() { Thread t = new Thread(this); t.start(); @@ -240,7 +219,7 @@ public void run() { boolean isError = false; lblStatus.setText("运行中..."); // 再运行一个进程控制进度条 - new BackRunnerShowProgress().start(); + new BW_ShowProgress().start(); try { crypto.Crypt(textFilePath.getText(), textKey.getText()); } catch (NullPointerException e) { @@ -254,6 +233,7 @@ public void run() { if (!isError) lblStatus.setText("成功。"); btnEncryptDecrypt.setEnabled(true); + crypto = new Crypto(); } } @@ -261,7 +241,7 @@ public void run() { /** * 控制进度条的显示 */ - class BackRunnerShowProgress implements Runnable { + class BW_ShowProgress implements Runnable { public void start() { Thread t = new Thread(this); t.start(); @@ -306,6 +286,7 @@ public void run() { textFilePath = new JTextField(); textFilePath.setBackground(new Color(-13487566)); textFilePath.setForeground(new Color(-7629154)); + textFilePath.setHorizontalAlignment(10); textFilePath.setText("支持拖拽文件至此"); mainPanel.add(textFilePath, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(300, 30), null, 0, false)); final JLabel label1 = new JLabel(); diff --git a/UI/Resource/About.txt b/UI/Resource/About.txt index ed5bc14..cff3a24 100644 --- a/UI/Resource/About.txt +++ b/UI/Resource/About.txt @@ -1,16 +1,21 @@ Crypto -Version 0.1.2 +Version 0.2.0 -Powered by Java / Intellj IDEA +Powered by Java / IntelliJ IDEA Lzhjk Presents +v0.2.0 新添加动画效果 加密算法: 密钥字符串的hashCode作为Random类的种子 对文件的每一个字节,取五次随机值对其进行异或运算 利用Random的伪随机数特性和异或运算的可逆性 - 缺陷很明显 + 缺陷是 简单的异或运算以及字符串生成哈希值可能的重复问题 + + +Any questions? + Please mail to xxlzhjkxx@gmail.com \ No newline at end of file