commit 93d343dcf52c6db439491d36eca1a5221b75da6e Author: free will <2647778488@qq.com> Date: Tue Aug 4 21:24:11 2020 +0800 在v0.1版本基础上,增加了通信加密功能 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2112b23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.gradle/ +build/ +out/ +ndn/pid/* +ndn/tpm/* +ndn/log.txt +.idea/ \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..db61f7e --- /dev/null +++ b/build.gradle @@ -0,0 +1,45 @@ +plugins { + id 'java' + id 'org.openjfx.javafxplugin' version '0.0.5' +} +apply plugin: 'java' +apply plugin: 'org.openjfx.javafxplugin' +group 'pkusz' +version '0.1' + +sourceCompatibility = 11 +targetCompatibility = 11 + +//指定编译编码 +tasks.withType(JavaCompile){ + options.encoding="UTF-8" +} + +repositories { + maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} +// mavenCentral() +} + +dependencies { +// compile files('lib/controlsfx-8.0.6_20.jar') + testCompile group: 'junit', name: 'junit', version: '4.12' + compile fileTree(dir: 'lib',includes: ['*jar']) + compile 'com.blankj:utilcode:1.10.0' + +} + +javafx { + version = "11.0.2" + modules = [ 'javafx.controls','javafx.fxml' ] +} + +jar{ + manifestContentCharset 'utf-8' + metadataCharset 'utf-8' + manifest{ + attributes "Main-Class": "cn.minoa.Main" + } + from{ + configurations.compile.collect{it.isDirectory()?it:zipTree(it)} + } +} diff --git a/lib/commons-codec-1.13.jar b/lib/commons-codec-1.13.jar new file mode 100644 index 0000000..bf6ccb3 Binary files /dev/null and b/lib/commons-codec-1.13.jar differ diff --git a/lib/jndn-0.23-sources.jar b/lib/jndn-0.23-sources.jar new file mode 100644 index 0000000..510c2ce Binary files /dev/null and b/lib/jndn-0.23-sources.jar differ diff --git a/lib/jndn-0.23.jar b/lib/jndn-0.23.jar new file mode 100644 index 0000000..774d474 Binary files /dev/null and b/lib/jndn-0.23.jar differ diff --git a/lib/json-20080701.jar b/lib/json-20080701.jar new file mode 100644 index 0000000..c9a093e Binary files /dev/null and b/lib/json-20080701.jar differ diff --git a/lib/sqlite-jdbc-3.30.1.jar b/lib/sqlite-jdbc-3.30.1.jar new file mode 100644 index 0000000..8583737 Binary files /dev/null and b/lib/sqlite-jdbc-3.30.1.jar differ diff --git a/ndn/BlockSuccessUsers.properties b/ndn/BlockSuccessUsers.properties new file mode 100644 index 0000000..ba345dd --- /dev/null +++ b/ndn/BlockSuccessUsers.properties @@ -0,0 +1,2 @@ +#commit changes: delete a prefix +#Fri Apr 17 20:54:49 CST 2020 diff --git a/ndn/FileBPRenewal.properties b/ndn/FileBPRenewal.properties new file mode 100644 index 0000000..8ae5cc6 --- /dev/null +++ b/ndn/FileBPRenewal.properties @@ -0,0 +1,5 @@ +#commit changes +#Sat Jun 13 21:01:12 CST 2020 +filePoint-17A14B227AA88D90777E474A4FB069FB=1700C7E9006964993B9205E1475D2971 +fileServerPath-17A14B227AA88D90777E474A4FB069FB=6FBECA975021EF837FA57E30E2385322 +fileName-17A14B227AA88D90777E474A4FB069FB=AAEE06DF89E40D8A3BC74AF731335E7C diff --git a/ndn/pri.txt b/ndn/pri.txt new file mode 100644 index 0000000..bcef396 --- /dev/null +++ b/ndn/pri.txt @@ -0,0 +1 @@ +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdQhZHnXQmXCTrLbzsN9ZamwLFp+lWvZAt6oBpHTio5ppqpe6FltShrGBLYeNU2N71deJk4CstcYksrCF9SmtjClIh9foM1aEdQpzBg1xrZ2TM7XQ1c/NjtIuAM/3JjLIhXjZL9AIIZnuS7t6f9V5H/HcIgA5yGgFK3plGHuIbCXSznPBVM8XeKzhb7jq9QQKSZuLwdhGNIIS90CqjsH3vi81vy0KvCaHO1Df4h0ia6RIXn9BcwK5pQovP0I0FnylSXjkeu4b9jE14lcgL0xUbWk1noPikHDJyHAugzXqqMfz93Fmdoi8ryu7T1wU1hOjAFQ6Zry712lCwwPj5Tw61AgMBAAECggEAf3efVkrDq4XSTxreiEfy2egLxMtjhnFXKty6ddQ73DJeK6rEq3mlri6cm1Edorf+WvM7JlpvBZMvL+lKsd+51ikQADixgcthMnY50NJY4BeE3vq9xD20rnBJHebWAtNnnAmmxkMhGK3R62hqIUU+t8Ie4WrE579Ll1J+gwMvx9qsdr2dKSjkChiLwa9fPYI0W4/nRJZ5gKLXl+n4rK/SPR7AdhZbzMWDKMqFChcsDqai3jSrcrR3A7DSzojF+9gKEamBPmd10THYulblSyq65nhhnbZcpP09gNv3r2R7xdnRgIUPcXO98+f5zjEbZa9F1/0RDaGPtXdQbrlqICLFUQKBgQDymP0bhF++pb1wuoKcJqFwIFPAaT6pb1YGbbPHci89rS+bt42BMLKrnor28h9bTEKmxqi5/tv3buzKBc+wJzMHdLswAD0HvBZV2ayQZFTosvSufM5qykpkwGVp0DymL4kwWu5GV7KMvBt3jFIt/UaK9QrkhBKOMC2C8ya6kE2FbwKBgQDpe0xn/ENTp93+/2APN9LmgqTrxDpgOuPZuIuU6o04CPbbhZYT6/S6XrviXeYHCH7doLSg4W7xBFEaLrVaWx0IcP2u4u41iPSVqBSkNSzuNcQVnSx5G3fMIk/dbqD5E83FN2clX5JJSkyD2VvSe0wJ8DsOs1U1PfA99KWMNhfEGwKBgQDECarm9JZmOgqWsLDgvDrSn9zt7F4tjwGmGjof3n6cSXniTHZ1wkNveaN0IH4ESeDGYlxwSKm6DhIcwottHpAW41ZsWJT4zh4Ca+8Nk3YlwI8G/WXUV/m75tlMo3CPHnSu9Zv8jduD7HIJrgOulC/QFGoj9TIFYps0aYaPVhK+0wKBgQDRUt2ZjKy/34xEBaiVfDL0vsQlK/Q6OhH1LGLXy8F4L3FCNm3qxmzOeghwVkbYn2E+SconQnU4V+puUtn8fBt3afXGSWXPUU4Wrm5zGcmCd2mpBxTMLzgkJ876cyiWpo6g9Z7f/uMkGhynSMPt3OS7NEl+UIFgDnvI0AFrx0NfaQKBgG9KCeHR+vA5D1P7Pmj/I0RfS5VYGwErIz4n6mDJezF+eawkSLp8cyyJ/EBE6XkITyN6CYr3bT4DNnN6rUx8RwwHkZ++h4sNeVkVxEqebGwBKyylXxZygRqWgDQRybDx8xP78aIMn0XJ74z/dD9w/Yos9W7WZdgBGlBGwt4Se1ew \ No newline at end of file diff --git a/ndn/pub.txt b/ndn/pub.txt new file mode 100644 index 0000000..53d8975 --- /dev/null +++ b/ndn/pub.txt @@ -0,0 +1 @@ +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UIWR510Jlwk6y287DfWWpsCxafpVr2QLeqAaR04qOaaaqXuhZbUoaxgS2HjVNje9XXiZOArLXGJLKwhfUprYwpSIfX6DNWhHUKcwYNca2dkzO10NXPzY7SLgDP9yYyyIV42S/QCCGZ7ku7en/VeR/x3CIAOchoBSt6ZRh7iGwl0s5zwVTPF3is4W+46vUECkmbi8HYRjSCEvdAqo7B974vNb8tCrwmhztQ3+IdImukSF5/QXMCuaUKLz9CNBZ8pUl45HruG/YxNeJXIC9MVG1pNZ6D4pBwychwLoM16qjH8/dxZnaIvK8ru09cFNYTowBUOma8u9dpQsMD4+U8OtQIDAQAB \ No newline at end of file diff --git a/ndn/pub_abc.txt b/ndn/pub_abc.txt new file mode 100644 index 0000000..e06c50c --- /dev/null +++ b/ndn/pub_abc.txt @@ -0,0 +1 @@ +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArQpGd3+hMWsc+Wbrt8RnMd175NkLASAHjdq1W2mySrgzTEzo4wWegcxp/Jgau0Chu4Sy4vRXfqjv8hIuUqaTmdETkmwv4RM6lTM+neDCV+vWolE+KlLAaBEPRW54EdkinIbLGeK69OWDVvAnkKVxma6WuZeVpbj6W76Cc90426OFEH8eHHWg8QlG1QX8Z+2AfO/SzRjL1UBvYNzMg3Yr1+5W/km8Zc/vj5zDBOh85dd9zvxh2lcRQeX0T9akdlygWyFc/MPwuka0Q24ie5EXC1z6KCJTa92cF8cj1Mnp7MLjosW5EqfN66wMpM5VMB7/0asufFP1u/B1rd0B7MJaDwIDAQAB \ No newline at end of file diff --git a/ndn/settings.properties b/ndn/settings.properties new file mode 100644 index 0000000..f6b8ec2 --- /dev/null +++ b/ndn/settings.properties @@ -0,0 +1,12 @@ +#\u4FEE\u6539\u914D\u7F6E\u6587\u4EF6 +#Sat Jun 13 22:34:20 CST 2020 +KeyName=/MYGOD +loginUserName=FDF5A6F1F2B78ADE3DC7700AEFEF5C0F +ndnBlockChainIPAddress=116.77.74.140 +ServerIPAddress=116.77.74.139 +ThreadNumberForFileTransfer=5 +ndnBlockChainPortAddress=9001 +DataPackagePrefix=/ndn/edu/pkusz/OA +LoginStatus=false +loginPassWord=A1B9652D9ED940B8FC63453C50CB3337 +ServerPortAddress=6363 diff --git a/resources/images/about_me.png b/resources/images/about_me.png new file mode 100644 index 0000000..668afe8 Binary files /dev/null and b/resources/images/about_me.png differ diff --git a/resources/images/cloud_file.png b/resources/images/cloud_file.png new file mode 100644 index 0000000..b03c3e0 Binary files /dev/null and b/resources/images/cloud_file.png differ diff --git a/resources/images/delete.png b/resources/images/delete.png new file mode 100644 index 0000000..66f2e44 Binary files /dev/null and b/resources/images/delete.png differ diff --git a/resources/images/emailFileIcon/f.png b/resources/images/emailFileIcon/f.png new file mode 100644 index 0000000..ed4b5c5 Binary files /dev/null and b/resources/images/emailFileIcon/f.png differ diff --git a/resources/images/entry_examination.png b/resources/images/entry_examination.png new file mode 100644 index 0000000..22c4bff Binary files /dev/null and b/resources/images/entry_examination.png differ diff --git a/resources/images/evection.png b/resources/images/evection.png new file mode 100644 index 0000000..4751a43 Binary files /dev/null and b/resources/images/evection.png differ diff --git a/resources/images/headPhotos/MYGOD.png b/resources/images/headPhotos/MYGOD.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/MYGOD.png differ diff --git a/resources/images/headPhotos/byj.png b/resources/images/headPhotos/byj.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/byj.png differ diff --git a/resources/images/headPhotos/default.png b/resources/images/headPhotos/default.png new file mode 100644 index 0000000..ea96cb0 Binary files /dev/null and b/resources/images/headPhotos/default.png differ diff --git a/resources/images/headPhotos/oa_wefree.png b/resources/images/headPhotos/oa_wefree.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/oa_wefree.png differ diff --git a/resources/images/headPhotos/qjm253.png b/resources/images/headPhotos/qjm253.png new file mode 100644 index 0000000..aac9272 Binary files /dev/null and b/resources/images/headPhotos/qjm253.png differ diff --git a/resources/images/headPhotos/qjm254.png b/resources/images/headPhotos/qjm254.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/qjm254.png differ diff --git a/resources/images/headPhotos/qjm5.png b/resources/images/headPhotos/qjm5.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/qjm5.png differ diff --git a/resources/images/headPhotos/test111.png b/resources/images/headPhotos/test111.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/test111.png differ diff --git a/resources/images/headPhotos/testKey_v1.png b/resources/images/headPhotos/testKey_v1.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/testKey_v1.png differ diff --git a/resources/images/headPhotos/testKey_v2.png b/resources/images/headPhotos/testKey_v2.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/testKey_v2.png differ diff --git a/resources/images/headPhotos/testKey_v3.png b/resources/images/headPhotos/testKey_v3.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/testKey_v3.png differ diff --git a/resources/images/headPhotos/wefree.png b/resources/images/headPhotos/wefree.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wefree.png differ diff --git a/resources/images/headPhotos/wefree70.png b/resources/images/headPhotos/wefree70.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wefree70.png differ diff --git a/resources/images/headPhotos/wefree73.png b/resources/images/headPhotos/wefree73.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wefree73.png differ diff --git a/resources/images/headPhotos/wefree_aac.png b/resources/images/headPhotos/wefree_aac.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wefree_aac.png differ diff --git a/resources/images/headPhotos/wf.png b/resources/images/headPhotos/wf.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf.png differ diff --git a/resources/images/headPhotos/wf0.png b/resources/images/headPhotos/wf0.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf0.png differ diff --git a/resources/images/headPhotos/wf002.png b/resources/images/headPhotos/wf002.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf002.png differ diff --git a/resources/images/headPhotos/wf003.png b/resources/images/headPhotos/wf003.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf003.png differ diff --git a/resources/images/headPhotos/wf004.png b/resources/images/headPhotos/wf004.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf004.png differ diff --git a/resources/images/headPhotos/wf0977.png b/resources/images/headPhotos/wf0977.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf0977.png differ diff --git a/resources/images/headPhotos/wf123.png b/resources/images/headPhotos/wf123.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf123.png differ diff --git a/resources/images/headPhotos/wf2.png b/resources/images/headPhotos/wf2.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf2.png differ diff --git a/resources/images/headPhotos/wf3.png b/resources/images/headPhotos/wf3.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf3.png differ diff --git a/resources/images/headPhotos/wf9.png b/resources/images/headPhotos/wf9.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf9.png differ diff --git a/resources/images/headPhotos/wf972.png b/resources/images/headPhotos/wf972.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf972.png differ diff --git a/resources/images/headPhotos/wf974.png b/resources/images/headPhotos/wf974.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf974.png differ diff --git a/resources/images/headPhotos/wf976.png b/resources/images/headPhotos/wf976.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf976.png differ diff --git a/resources/images/headPhotos/wf977.png b/resources/images/headPhotos/wf977.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf977.png differ diff --git a/resources/images/headPhotos/wf978.png b/resources/images/headPhotos/wf978.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf978.png differ diff --git a/resources/images/headPhotos/wf979.png b/resources/images/headPhotos/wf979.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wf979.png differ diff --git a/resources/images/headPhotos/wgh1.png b/resources/images/headPhotos/wgh1.png new file mode 100644 index 0000000..de71a9e Binary files /dev/null and b/resources/images/headPhotos/wgh1.png differ diff --git a/resources/images/headPhotos/wgh2.png b/resources/images/headPhotos/wgh2.png new file mode 100644 index 0000000..a092c9f Binary files /dev/null and b/resources/images/headPhotos/wgh2.png differ diff --git a/resources/images/headPhotos/wgh3.png b/resources/images/headPhotos/wgh3.png new file mode 100644 index 0000000..ea96cb0 Binary files /dev/null and b/resources/images/headPhotos/wgh3.png differ diff --git a/resources/images/headPhotos/wgh333.png b/resources/images/headPhotos/wgh333.png new file mode 100644 index 0000000..ea96cb0 Binary files /dev/null and b/resources/images/headPhotos/wgh333.png differ diff --git a/resources/images/headPhotos/wgh4.png b/resources/images/headPhotos/wgh4.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh4.png differ diff --git a/resources/images/headPhotos/wgh43.png b/resources/images/headPhotos/wgh43.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh43.png differ diff --git a/resources/images/headPhotos/wgh45.png b/resources/images/headPhotos/wgh45.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh45.png differ diff --git a/resources/images/headPhotos/wgh49.png b/resources/images/headPhotos/wgh49.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh49.png differ diff --git a/resources/images/headPhotos/wgh5.png b/resources/images/headPhotos/wgh5.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh5.png differ diff --git a/resources/images/headPhotos/wgh6.png b/resources/images/headPhotos/wgh6.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wgh6.png differ diff --git a/resources/images/headPhotos/wihteabcc.png b/resources/images/headPhotos/wihteabcc.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/wihteabcc.png differ diff --git a/resources/images/headPhotos/yinfeng.png b/resources/images/headPhotos/yinfeng.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/yinfeng.png differ diff --git a/resources/images/headPhotos/王锋.png b/resources/images/headPhotos/王锋.png new file mode 100644 index 0000000..8b8296b Binary files /dev/null and b/resources/images/headPhotos/王锋.png differ diff --git a/resources/images/headPhotos_用于测试/Sunny.png b/resources/images/headPhotos_用于测试/Sunny.png new file mode 100644 index 0000000..4d69fc0 Binary files /dev/null and b/resources/images/headPhotos_用于测试/Sunny.png differ diff --git a/resources/images/headPhotos_用于测试/default.png b/resources/images/headPhotos_用于测试/default.png new file mode 100644 index 0000000..4d69fc0 Binary files /dev/null and b/resources/images/headPhotos_用于测试/default.png differ diff --git a/resources/images/headPhotos_用于测试/oa_wefree.png b/resources/images/headPhotos_用于测试/oa_wefree.png new file mode 100644 index 0000000..7f13bea Binary files /dev/null and b/resources/images/headPhotos_用于测试/oa_wefree.png differ diff --git a/resources/images/headPhotos_用于测试/qjm253.png b/resources/images/headPhotos_用于测试/qjm253.png new file mode 100644 index 0000000..7dc70fc Binary files /dev/null and b/resources/images/headPhotos_用于测试/qjm253.png differ diff --git a/resources/images/headPhotos_用于测试/qjm254.png b/resources/images/headPhotos_用于测试/qjm254.png new file mode 100644 index 0000000..7dc70fc Binary files /dev/null and b/resources/images/headPhotos_用于测试/qjm254.png differ diff --git a/resources/images/headPhotos_用于测试/wefree.png b/resources/images/headPhotos_用于测试/wefree.png new file mode 100644 index 0000000..d88f2f4 Binary files /dev/null and b/resources/images/headPhotos_用于测试/wefree.png differ diff --git a/resources/images/headPhotos_用于测试/wgh1.png b/resources/images/headPhotos_用于测试/wgh1.png new file mode 100644 index 0000000..6a1db80 Binary files /dev/null and b/resources/images/headPhotos_用于测试/wgh1.png differ diff --git a/resources/images/headPhotos_用于测试/wgh2.png b/resources/images/headPhotos_用于测试/wgh2.png new file mode 100644 index 0000000..765c17d Binary files /dev/null and b/resources/images/headPhotos_用于测试/wgh2.png differ diff --git a/resources/images/headPhotos_用于测试/wgh3.png b/resources/images/headPhotos_用于测试/wgh3.png new file mode 100644 index 0000000..774234a Binary files /dev/null and b/resources/images/headPhotos_用于测试/wgh3.png differ diff --git a/resources/images/ic_launcher-web.png b/resources/images/ic_launcher-web.png new file mode 100644 index 0000000..2128b74 Binary files /dev/null and b/resources/images/ic_launcher-web.png differ diff --git a/resources/images/index.png b/resources/images/index.png new file mode 100644 index 0000000..8dded7b Binary files /dev/null and b/resources/images/index.png differ diff --git a/resources/images/leave_office.png b/resources/images/leave_office.png new file mode 100644 index 0000000..14ea2d9 Binary files /dev/null and b/resources/images/leave_office.png differ diff --git a/resources/images/logo.png b/resources/images/logo.png new file mode 100644 index 0000000..b877cba Binary files /dev/null and b/resources/images/logo.png differ diff --git a/resources/images/logo_mini.png b/resources/images/logo_mini.png new file mode 100644 index 0000000..2128b74 Binary files /dev/null and b/resources/images/logo_mini.png differ diff --git a/resources/images/mImage/left.png b/resources/images/mImage/left.png new file mode 100644 index 0000000..03bae87 Binary files /dev/null and b/resources/images/mImage/left.png differ diff --git a/resources/images/mImage/right.png b/resources/images/mImage/right.png new file mode 100644 index 0000000..ce1052c Binary files /dev/null and b/resources/images/mImage/right.png differ diff --git a/resources/images/mImage/test_headPhoto.png b/resources/images/mImage/test_headPhoto.png new file mode 100644 index 0000000..2128b74 Binary files /dev/null and b/resources/images/mImage/test_headPhoto.png differ diff --git a/resources/images/mail_list.png b/resources/images/mail_list.png new file mode 100644 index 0000000..077a98f Binary files /dev/null and b/resources/images/mail_list.png differ diff --git a/resources/images/message.png b/resources/images/message.png new file mode 100644 index 0000000..d62c112 Binary files /dev/null and b/resources/images/message.png differ diff --git a/resources/images/message_list.png b/resources/images/message_list.png new file mode 100644 index 0000000..077a98f Binary files /dev/null and b/resources/images/message_list.png differ diff --git a/resources/images/mine.png b/resources/images/mine.png new file mode 100644 index 0000000..96fdbc6 Binary files /dev/null and b/resources/images/mine.png differ diff --git a/resources/images/myFileIcon/d.png b/resources/images/myFileIcon/d.png new file mode 100644 index 0000000..1ad79a4 Binary files /dev/null and b/resources/images/myFileIcon/d.png differ diff --git a/resources/images/myFileIcon/f.png b/resources/images/myFileIcon/f.png new file mode 100644 index 0000000..ed4b5c5 Binary files /dev/null and b/resources/images/myFileIcon/f.png differ diff --git a/resources/images/myFileIcon/o.png b/resources/images/myFileIcon/o.png new file mode 100644 index 0000000..71c7015 Binary files /dev/null and b/resources/images/myFileIcon/o.png differ diff --git a/resources/images/notification.png b/resources/images/notification.png new file mode 100644 index 0000000..81a2951 Binary files /dev/null and b/resources/images/notification.png differ diff --git a/resources/images/pku.jpg b/resources/images/pku.jpg new file mode 100644 index 0000000..092dcdf Binary files /dev/null and b/resources/images/pku.jpg differ diff --git a/resources/images/pku_m.png b/resources/images/pku_m.png new file mode 100644 index 0000000..502bbc4 Binary files /dev/null and b/resources/images/pku_m.png differ diff --git a/resources/images/reimbursement.png b/resources/images/reimbursement.png new file mode 100644 index 0000000..2721176 Binary files /dev/null and b/resources/images/reimbursement.png differ diff --git a/resources/images/setting_gear.png b/resources/images/setting_gear.png new file mode 100644 index 0000000..766883b Binary files /dev/null and b/resources/images/setting_gear.png differ diff --git a/resources/images/work_summary.png b/resources/images/work_summary.png new file mode 100644 index 0000000..18dc55f Binary files /dev/null and b/resources/images/work_summary.png differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ebc4822 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'CUTV_FileSystem' +rootProject.name = 'CUTV_FileSystem' +rootProject.name = 'CUTV_FileSystem' + diff --git a/src/main/java/cn/minoa/Main.java b/src/main/java/cn/minoa/Main.java new file mode 100644 index 0000000..60e44dd --- /dev/null +++ b/src/main/java/cn/minoa/Main.java @@ -0,0 +1,11 @@ +package cn.minoa; +// 放置主函数入口。 +// 解决了“缺少运行JavaFX组件”的报错. + +import javafx.application.Application; + +public class Main { + public static void main(String[] args) { + Application.launch(MainApp.class); + } +} diff --git a/src/main/java/cn/minoa/MainApp.java b/src/main/java/cn/minoa/MainApp.java new file mode 100644 index 0000000..b1eff68 --- /dev/null +++ b/src/main/java/cn/minoa/MainApp.java @@ -0,0 +1,1794 @@ +package cn.minoa; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; + +import cn.minoa.dataRequestInterface.*; +import cn.minoa.model.*; +import cn.minoa.util.*; +import cn.minoa.view.NavigationBarController; +import cn.minoa.view.cloudfile.FileShareController; +import cn.minoa.view.home.*; +import cn.minoa.view.login.KeepLoginStatus; +import cn.minoa.view.login.LoginOverviewController; +import cn.minoa.view.login.RegisterOverviewController; +import cn.minoa.view.login.SettingOverviewController; +import cn.minoa.view.message.ChatBoxController; +import cn.minoa.view.message.MessageListController; +import cn.minoa.view.mine.*; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.WindowEvent; +import org.json.JSONException; +import org.json.JSONObject; + +public class MainApp extends Application { + + private Stage primaryStage; + // 登录界面控制类 + LoginOverviewController loginerController; + // 一级导航栏控制类 + private BorderPane navigationBar; + private NavigationBarController barController; + // 二级导航栏控制类 + private MessageListController messageBarController; + // 三级主面板控制类 + private ChatBoxController chatBoxController; + private MyCloudFileController myCloudFileController; + + // 记录当前的二级导航栏 + private String currentSecondPane; + private final String SECOND_NULLPANE; + private final String SECOND_HOMEPANE; + private final String SECOND_MESSAGEPANE; + private final String SECOND_MINEPANE; + // 记录当前的主面板状态 + public String currentMainPane;//当前渲染的主面板界面 + public String currentReimPane;//当前报销界面的数据状态(收/发) + public String currentEmailPane;//当前邮件界面的数据状态(收/发) + public final String MAIN_CHATBOXPANE;//聊天界面 + public final String MAIN_NULLPANE;//空界面 + public final String MAIN_NOTICEPANE;//通告界面 + public final String MAIN_EMAILPANE;//邮件界面 + public final String MAIN_EMAILPANE_REC;//邮件收件箱 + public final String MAIN_EMAILPANE_SEND;//邮件发件箱 + public final String MAIN_CLOUDFILEPANE;//云文件界面 + public final String MAIN_ABOUTMINEPANE;//关于我界面 + public final String MAIN_REIMPANE;//报销界面 + public final String MAIN_REIMPANE_REC;//接收到的报销 + public final String MAIN_REIMPANE_SEND;//申请的报销 + public final String MAIN_REIMDEALPANE;//报销审核界面 + + public final String MAIN_FILESHAREPANE;//文件分享界面 + + // 判断联系人列表是否已经被完整加载的锁(保证刷新小红点的时候联系人列表已经产生) + private boolean contactListFinished; + // 判断聊天窗口是否已经被完整加载的锁(保证得到新消息时 在控制器已经被加载出来后才更新气泡) + private boolean chatBoxIniFinished; // 标志ChatBox是否初始化完成 + + // 服务器数据接口 + public volatile static MinoaDataAPI minoaDataAPI; + // 唯一标识数据请求 + // private Long dataReqId; + private static AtomicLong dataReqId = new AtomicLong(0); + + // 服务器数据接口参数信息 + public String minoaServerIpString; + public Integer minoaServerPortInteger; + public static String NDNBlockChainIPAddress; + public static Integer NDNBlockChainPortAddress; + public String globalPrefixString; + public Integer threadNumberForFileTransfer; //文件传输使用的线程数目 +// public String pidDbPathString; + + // 密钥导出文件的默认文件名称: username_KeyFileNameString + public static String KeyFileNameString="SecretKeyMIN"; + + // 加密通信中,动态生成的AES密钥 + public static String aesKey_16Byte; + + // 保存登录者信息 + public static Loginer loginer; + // 保存联系人列表数据 + public ObservableList contactListData = FXCollections.observableArrayList(); + // 保存与所有联系人的消息记录 + public Map> messageLogMap; + // 存储“登录时未读消息”(之所以有这个,是因为初始化log时已经将未读消息初始化进去了,登录时候获取的未读消息不能在阅读之后按照正常逻辑再次放入Log) + public NotReadMessageList iniNotReadMessageListWhenLogin; + // 维持与所有人的“未读消息记录” + public NotReadMessageList notReadMessageList; + // 保存通知列表 + public ObservableList noticeList = FXCollections.observableArrayList(); + + // 文件下载默认保存到windows本地的路径 + public final String fileLocalPath; + // 文件分片包含的最大字节数 + public static final Integer sliceSize = 7000; + // 文件上传默认上传到服务器的路径[用户根目录] + public static final String fileServerBinPath = "/"; + // 邮件中的附件文件上传到服务器的路径[/share] + public final String sharedFileServerPath; + + // 服务器时间戳-客户端时间戳 + public static long timeStampBias=0; + + // 用户头像的默认保存位置 + public final String userHeadPhotoBinPath; + +// // 下载文件:保存当前已下载的文件数据分片,为进度条显示服务 +// public static AtomicInteger currentDownloadedDataSlices = new AtomicInteger(0); +// // 上传文件:保存当前已下载的文件数据分片,为进度条显示服务 +// public static AtomicInteger currentUploadedDataSlices = new AtomicInteger(0); + + + // 进度条更新任务[传入子线程] + Task copyWorker; + // 标记是否是手动取消了上传或下载操作【false表示未取消,true表示取消】 + public volatile static boolean isCancelFileTransferFlag; + // 上传文件进度条stage + private Stage uploadFileDialogStage; + // 下载文件进度条stage + private Stage downloadFileDialogStage; + // 进度监听线程 + private Thread listenThread; + // 上传客户端 + private UpLoadClientBPRenewal upLoadClient; + // 下载客户端 + private DownLoadClientBPRenewal downLoadClient; + + + // 决定心跳包是否需要判断登录状态是否失效 + public boolean isNeedKnowLoginStatus=false; + + public MainApp() { + // 初始化ndn目录 + GenerateSettingFiles.generateInitFiles(); + // 加载配置文件 + loadSettingProperties(); + // 初始化一些数据 + minoaDataAPI = new MinoaDataAPI(this); + messageLogMap = new TreeMap>(); + iniNotReadMessageListWhenLogin = new NotReadMessageList(); + notReadMessageList = new NotReadMessageList(); + loginer = new Loginer(); + contactListFinished = false; + chatBoxIniFinished = false; + fileLocalPath = "E:\\"; +// fileServerBinPath = "/"; + sharedFileServerPath = "/share/"; + System.out.println("客户端根目录:" + getClientPath()); + userHeadPhotoBinPath = getClientPath() + "\\resources\\images\\headPhotos\\"; +// System.out.println(userHeadPhotoBinPath); +// sliceSize = 7000; + isCancelFileTransferFlag = false; + // 初始化AES密钥 + aesKey_16Byte=Aes.getRandomString(16); + System.out.println("AES KEY (16Bytes): "+aesKey_16Byte); + + // 初始化记录当前显示的面板的字符串 + // 固定字符串 + SECOND_NULLPANE = "SECOND_NULLPANE"; + SECOND_HOMEPANE = "SECOND_HOMEPANE"; + SECOND_MESSAGEPANE = "SECOND_MESSAGEPANE"; + SECOND_MINEPANE = "SECOND_MINEPANE"; + MAIN_CHATBOXPANE = "MAIN_CHATBOXPANE"; + MAIN_NULLPANE = "MAIN_NULLPANE"; + MAIN_NOTICEPANE = "MAIN_NOTICEPANE"; + MAIN_EMAILPANE = "MAIN_EMAILPANE"; + MAIN_EMAILPANE_REC = "MAIN_EMAILPANE_REC"; + MAIN_EMAILPANE_SEND = "MAIN_EMAILPANE_SEND"; + MAIN_CLOUDFILEPANE = "MAIN_CLOUDFILEPANE"; + MAIN_ABOUTMINEPANE = "MAIN_ABOUTMINEPANE"; + MAIN_REIMPANE = "MAIN_REIMPANE"; + MAIN_REIMPANE_REC = "MAIN_REIMPANE_REC"; + MAIN_REIMPANE_SEND = "MAIN_REIMPANE_SEND"; + MAIN_REIMDEALPANE = "MAIN_REIMDEALPANE"; + MAIN_FILESHAREPANE = "MAIN_FILESHAREPANE"; + // 记录字符串 + currentSecondPane = SECOND_NULLPANE; + currentMainPane = MAIN_NULLPANE; + + System.out.println("中文 bytelength: " + StringByteLengthUtil.getByteLength("中文")); + System.out.println("en bytelength: " + StringByteLengthUtil.getByteLength("en")); + } + + private static String getTimeSyncJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/timeSync"; + try { + jsonObject.put("command", commandString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 初始化时间戳 + public static void iniTimeStampBias(){ + // 先整理好时间戳请求json + Long seqLong = MainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getTimeSyncJson()); + + // 取当前客户端时间 + long clientTime=System.currentTimeMillis()/1000; + + // 获取当前服务器时间 + MainApp.minoaDataAPI.executeOrder("/timeSync", orderInfo,true); + // 根据结果判断是否登录成功 + ResponseData responseData = MainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + responseData.printSelf(); + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + if (codeInteger == 200) { + long serverTime = jsonObject.getLong("data"); + timeStampBias=serverTime-clientTime; + }else{ + timeStampBias=0; + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + timeStampBias=0; + } + } + + // 加载可修改属性的配置文件 + public void loadSettingProperties() { + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream(this.getClientPath() + File.separator + "ndn" + File.separator + "settings.properties")); +// inputStream = MainApp.class.getClassLoader().getResourceAsStream("/ndn/settings.properties"); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + // 解决中文编码乱码问题 + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); +// settingProperties.load(inputStream); + settingProperties.load(bf); + String serverIPAddress = settingProperties.getProperty("ServerIPAddress"); + String serverPortAddress = settingProperties.getProperty("ServerPortAddress"); + String NDNBlockChainIPAddressString=settingProperties.getProperty("ndnBlockChainIPAddress"); + String NDNBlockChainPortAddressString=settingProperties.getProperty("ndnBlockChainPortAddress"); + String dataPackagePrefix = settingProperties.getProperty("DataPackagePrefix"); + String threadNumberString = settingProperties.getProperty("ThreadNumberForFileTransfer"); + System.out.println("ServerIPAddress: " + serverIPAddress); + System.out.println("ServerPortAddress: " + serverPortAddress); + System.out.println("NDNBlockChainIPAddress: "+NDNBlockChainIPAddressString); + System.out.println("NDNBlockChainPortAddress: "+NDNBlockChainPortAddressString); + System.out.println("DataPackagePrefix: " + dataPackagePrefix); + System.out.println("ThreadNumberString: " + threadNumberString); + this.minoaServerIpString = serverIPAddress; + this.minoaServerPortInteger = Integer.parseInt(serverPortAddress); + this.NDNBlockChainIPAddress=NDNBlockChainIPAddressString; + this.NDNBlockChainPortAddress=Integer.parseInt(NDNBlockChainPortAddressString); + this.globalPrefixString = dataPackagePrefix; + this.threadNumberForFileTransfer = Integer.parseInt(threadNumberString); + } catch (IOException e) { + e.printStackTrace(); + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // 获取一个新的数据请求标识 + public static Long getNewDataReqId() { + return dataReqId.incrementAndGet(); + } + + // 获取项目根目录路径 + public static String getClientPath() { + File directory = new File("");// 参数为空 + String path = ""; + try { + path = directory.getCanonicalPath(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } +// System.out.println("客户端根目录:" + path); + return path; + } + + /* + * @return + */ + public ObservableList getContactListData() { + return contactListData; + } + + /* + * @return + */ + public ObservableList getCurrentMessageLogBySelectedPersonListData(String selectedPersonName) { +// System.out.println("MainApp-getCurrentMessageLogBySelectedPersonListData-selectedPersonName: "+selectedPersonName); +// System.out.println(messageLogMap.get(selectedPersonName).size()); + return messageLogMap.get(selectedPersonName); + } + + /* + * @return + */ + public ObservableList getNoticeListData() { + return noticeList; + } + + // 更新全部消息=>暂时没用到 + public void refreshMessageLog() { + loginerController.updateMessageLog(); + } + + // 开启线程,监听是否有消息或通知或审批的更新=>被登录控制器调用 + public void creatThreadToRefreshMessageLog() { + minoaDataAPI.bindNews(); + } + + // 判断minoaAPI中的face是否启动,如果未开启,说明IP有问题,返回false + public static boolean isFaceLinked(){ + return minoaDataAPI.runFace(); + } + + // 下载文件到本地缺省目录下[E盘根目录] + public Integer downloadFileByPathfilename(String pathfileName) { + return loginerController.downloadFile(pathfileName); + } + + // 登录失效:弹窗提醒,点击确定之后跳转到登录界面 + public void reLogin(){ + // 非FX线程调用FX的UI组件 + Platform.runLater(() -> { + // 首先,修改本地登录状态 + KeepLoginStatus.setLoginStatus(false); + // 然后,弹窗提醒 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("登录状态失效"); + alert.setHeaderText("登录状态失效"); + alert.setContentText("您的登录状态失效,请重新登录。"); + alert.showAndWait(); + // 最后,展示登录界面 + showLoginOverview(); + }); + } + + // 下载文件到指定目录下 +// public Integer downloadFileByPathfilename(String pathfileName, String localDir) { +// return loginerController.downloadFileToSpecifiedDir(pathfileName, localDir); +// } + + // 显示进度条并下载文件到指定目录下 + public void downloadFileByPathfilenameWithProgressBar(String pathfileName, String localDir) { +// currentDownloadedDataSlices.set(0); + isCancelFileTransferFlag = false; + // 创建进度条子Stage + downloadFileDialogStage = new Stage(); + downloadFileDialogStage.setTitle("FileSystem-文件传输"); + downloadFileDialogStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + downloadFileDialogStage.initStyle(StageStyle.TRANSPARENT); + downloadFileDialogStage.initModality(Modality.WINDOW_MODAL); + downloadFileDialogStage.initOwner(primaryStage); + + Group root = new Group(); + Scene scene = new Scene(root, 530, 220, Color.WHITE); + downloadFileDialogStage.setScene(scene); + + BorderPane mainPane = new BorderPane(); + mainPane.setPrefHeight(200); + mainPane.setPrefWidth(525); + root.getChildren().add(mainPane); + + final Label label = new Label("文件传输进度:"); + final ProgressBar progressBar = new ProgressBar(0); + progressBar.setPrefWidth(300); + Long filelen=loginerController.getServerFileLength(pathfileName); + Integer cycle = (int)(filelen / sliceSize); + if (filelen % sliceSize != 0) { + cycle++; + } + final Integer sliceNum=cycle; +// final Text text = new Text("0/" + cycle.toString()); + final Text text = new Text("0%"); + text.setWrappingWidth(50); + + Text nullTextTop=new Text("");//空text,控制位置 + Text nullTextLeft=new Text("");//空text,控制位置 + // 文件名称 + final Label filenamelabel = new Label("文件名称:"); + final Text filenameText = new Text(); + String filename=pathfileName; + if(filename.length()>50){ + filename=filename.substring(0,50)+"..."; + } + filenameText.setText(filename); + final HBox fnhb = new HBox(); + fnhb.setSpacing(10); + fnhb.setAlignment(Pos.CENTER_LEFT); + fnhb.getChildren().addAll(filenamelabel, filenameText); + // 文件大小 + final Label filesizeLabel = new Label("文件大小:"); + final Text filesizeText = new Text(); + String filesize= FileUtil.sizeToShowSize((long)filelen); + filesizeText.setText(filesize); + final HBox fshb = new HBox(); + fshb.setSpacing(10); + fshb.setAlignment(Pos.CENTER_LEFT); + fshb.getChildren().addAll(filesizeLabel, filesizeText); +// fshb.setLayoutX(50); + // 两个合并 + final VBox vb=new VBox(); + vb.setSpacing(25); + vb.setAlignment(Pos.CENTER_LEFT); + vb.getChildren().addAll(nullTextTop,fnhb,fshb); + HBox bigHb=new HBox(); + bigHb.setSpacing(38); + bigHb.setAlignment(Pos.CENTER_LEFT); + bigHb.getChildren().setAll(nullTextLeft,vb); +// vb.setLayoutX(50); + mainPane.setTop(bigHb); + + final HBox hb = new HBox(); + hb.setSpacing(10); + hb.setAlignment(Pos.CENTER); + hb.getChildren().addAll(label, progressBar,text); + mainPane.setCenter(hb); + + final Button startButton = new Button("开始传输"); + final Button cancelButton = new Button("取消传输"); + final Button sureButton = new Button("确认完成"); + sureButton.setDisable(true); + final HBox hb2 = new HBox(); + hb2.setSpacing(50); + hb2.setAlignment(Pos.CENTER); +// hb2.getChildren().addAll(startButton); + hb2.getChildren().addAll(startButton, cancelButton,sureButton); + mainPane.setBottom(hb2); + startButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + startButton.setDisable(true); + progressBar.setProgress(0); + cancelButton.setDisable(false); + sureButton.setDisable(true); + copyWorker = createDownWorkerBasedWindow(pathfileName, localDir,sliceNum, cancelButton,sureButton,text); + progressBar.progressProperty().unbind(); + progressBar.progressProperty().bind(copyWorker.progressProperty()); + copyWorker.messageProperty().addListener(new ChangeListener() { + public void changed(ObservableValue observable, + String oldValue, String newValue) { + System.out.println(newValue); + } + }); + new Thread(copyWorker).start(); + } + }); + cancelButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + isCancelFileTransferFlag = true; + // 再次关闭进度条监听线程 + listenThread.interrupt(); + // 保存断点信息 + downLoadClient.setPointInfo(); + // 关闭该次传输 + downLoadClient.closeSelf(); + // 弹窗关闭 + downloadFileDialogStage.close(); + } + }); + sureButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + downloadFileDialogStage.close(); + } + }); + // 打开文件传输Stage + downloadFileDialogStage.showAndWait(); + } + + // 文件数据下载TASK:滑动窗口 + public Task createDownWorkerBasedWindow(String filename, String localDir,Integer sliceNum, Button button,Button sureButton,Text text) { + return new Task() { + @Override + protected Object call() throws Exception { + Long startTime = System.currentTimeMillis(); + // 滑动窗口协议下载文件 +// DownLoadClient downLoadClient = new DownLoadClient(filename,localDir,sliceNum); +// DownLoadClientBasedFacePool downLoadClient=new DownLoadClientBasedFacePool(filename, +// localDir,sliceNum,threadNumberForFileTransfer); + downLoadClient=new DownLoadClientBPRenewal(filename, + localDir,sliceNum,threadNumberForFileTransfer); + //启动一个线程监听更新的传输进度值 + listenThread = new Thread(new Runnable() { + @Override + public void run() { + Long sliceNumLong=sliceNum.longValue(); + // TODO Auto-generated method stub + while (true) { + Long progressInteger = downLoadClient.recvCount.get(); + System.out.println("当前已经下载的数据片个数:" + progressInteger); + Double progressPercentDouble=Math.floor((progressInteger*100)/sliceNumLong); + Integer progressPercentInteger=progressPercentDouble.intValue(); + String textString=progressPercentInteger.toString()+"%"; + // 更新进度条 +// String textString=progressInteger.toString()+ "/" + sliceNumLong.toString(); + if (!text.getText().equals(textString)) { + updateProgress(progressInteger, sliceNumLong); + text.setText(textString); + } + //如果当前进度条 + if (progressInteger.equals(sliceNumLong)) { + updateProgress(1,1); + text.setText("100%"); + System.out.println("***************监听进度条的线程结束监听*************"); + break; + } + try { + Thread.sleep(30); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + text.setText("100%"); + updateProgress(1,1); + System.out.println("***************监听进度条的线程结束监听*************"); + break; + } + } + } + }); + listenThread.start(); + // 下载文件 + boolean res = downLoadClient.downloadData(); + listenThread.interrupt(); + Long endTime = System.currentTimeMillis(); + System.out.println("耗时:" + (endTime - startTime) + "ms"); + LoggerUtil.insertFileAction(filename,"下载",(endTime - startTime) + "ms"); + // 每个分片都上传成功 + if (res) { + System.out.println("已成功将文件 " + filename + " 下载到本地" + localDir + "目录下。"); + } else { + System.out.println("由于未知原因,服务器端文件" + filename + "下载失败。"); + } + //设置确认按钮 + button.setDisable(true); + sureButton.setDisable(false); + return true; + } + }; + } + + // 显示进度条并上传文件到指定目录 + public void uploadFileToServerPathWithProgressBar(File file, String serverpath) { +// currentUploadedDataSlices.set(0); + isCancelFileTransferFlag = false; + // 创建进度条子Stage + uploadFileDialogStage = new Stage(); + uploadFileDialogStage.setTitle("FileSystemWindows-文件传输"); + uploadFileDialogStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + uploadFileDialogStage.initStyle(StageStyle.TRANSPARENT); + uploadFileDialogStage.initModality(Modality.WINDOW_MODAL); + uploadFileDialogStage.initOwner(primaryStage); + + Group root = new Group(); + Scene scene = new Scene(root, 530, 220, Color.WHITE); + uploadFileDialogStage.setScene(scene); + + BorderPane mainPane = new BorderPane(); + mainPane.setPrefHeight(200); + mainPane.setPrefWidth(525); + root.getChildren().add(mainPane); + + Text nullTextTop=new Text("");//空text,控制位置 + Text nullTextLeft=new Text("");//空text,控制位置 + // 文件名称 + final Label filenamelabel = new Label("文件名称:"); + final Text filenameText = new Text(); + String filename=file.getAbsolutePath(); + if(filename.length()>50){ + filename=filename.substring(0,50)+"..."; + } + filenameText.setText(filename); + final HBox fnhb = new HBox(); + fnhb.setSpacing(10); + fnhb.setAlignment(Pos.CENTER_LEFT); + fnhb.getChildren().addAll(filenamelabel, filenameText); + // 文件大小 + final Label filesizeLabel = new Label("文件大小:"); + final Text filesizeText = new Text(); + String filesize= FileUtil.sizeToShowSize(file.length()); + filesizeText.setText(filesize); + final HBox fshb = new HBox(); + fshb.setSpacing(10); + fshb.setAlignment(Pos.CENTER_LEFT); + fshb.getChildren().addAll(filesizeLabel, filesizeText); +// fshb.setLayoutX(50); + // 两个合并 + final VBox vb=new VBox(); + vb.setSpacing(25); + vb.setAlignment(Pos.CENTER_LEFT); + vb.getChildren().addAll(nullTextTop,fnhb,fshb); + HBox bigHb=new HBox(); + bigHb.setSpacing(38); + bigHb.setAlignment(Pos.CENTER_LEFT); + bigHb.getChildren().setAll(nullTextLeft,vb); +// vb.setLayoutX(50); + mainPane.setTop(bigHb); + + final Label label = new Label("文件传输进度:"); + final ProgressBar progressBar = new ProgressBar(0); + progressBar.setPrefWidth(300); +// long filelen = file.length(); +// int cycle = (int) (filelen / sliceSize); +// if (filelen % sliceSize != 0) { +// cycle++; +// } +// final Text text = new Text("0/" + cycle); + final Text text = new Text("0%" ); + text.setWrappingWidth(50); + final HBox hb = new HBox(); + hb.setSpacing(10); + hb.setAlignment(Pos.CENTER); + hb.getChildren().addAll(label, progressBar, text); + mainPane.setCenter(hb); + + final Button startButton = new Button("开始传输"); + final Button cancelButton = new Button("取消传输"); + final Button sureButton = new Button("确认完成"); + sureButton.setDisable(true); + final HBox hb2 = new HBox(); + hb2.setSpacing(50); + hb2.setAlignment(Pos.CENTER); +// hb2.getChildren().addAll(startButton); + hb2.getChildren().addAll(startButton, cancelButton,sureButton); + mainPane.setBottom(hb2); + startButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + startButton.setDisable(true); + progressBar.setProgress(0); + cancelButton.setDisable(false); +// copyWorker = createWorker(file, serverpath, cancelButton, text); + copyWorker = createUploadWorkerBasedWindow(file, serverpath, cancelButton, sureButton,text); + progressBar.progressProperty().unbind(); + progressBar.progressProperty().bind(copyWorker.progressProperty()); + copyWorker.messageProperty().addListener(new ChangeListener() { + public void changed(ObservableValue observable, + String oldValue, String newValue) { + System.out.println(newValue); + } + }); + new Thread(copyWorker).start(); + } + }); + cancelButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + // 手动取消了传输 + isCancelFileTransferFlag = true; + // 再次关闭进度条监听线程 + listenThread.interrupt(); + // 上传信息保存到本地 + upLoadClient.setPointInfo(); + // 关闭该次传输 + upLoadClient.closeSelf(); + // 关闭弹窗 + uploadFileDialogStage.close(); + } + }); + sureButton.setOnAction(new EventHandler() { + public void handle(ActionEvent event) { + // 确认完成了传输 + uploadFileDialogStage.close(); + } + }); + // 打开文件传输Stage + uploadFileDialogStage.showAndWait(); +// }); + } + + // 文件数据上传TASK:滑动窗口 + public Task createUploadWorkerBasedWindow(File file, String serverpath, Button button,Button sureButton, Text text) { + return new Task() { + @Override + protected Object call() throws Exception { + //获取要上传文件的文件长度,计算上传次数 + long filelen = file.length(); + int cycle = (int) (filelen / sliceSize); + if (filelen % sliceSize != 0) { + cycle++; + } + System.out.println("file-len: " + filelen); + System.out.println("cycle: " + cycle); + Long startTime = System.currentTimeMillis(); + // 滑动窗口协议上传文件 +// UpLoadClient upLoadClient = new UpLoadClient(file, serverpath); + // 增加了facepool支持的滑动窗口协议 +// UpLoadClientBasedFacePool upLoadClient=new UpLoadClientBasedFacePool(file, +// serverpath,threadNumberForFileTransfer); + // 进一步增加了断点续传支持 + upLoadClient=new UpLoadClientBPRenewal(file, + serverpath,threadNumberForFileTransfer); + //启动一个线程监听更新的传输进度值 + listenThread = new Thread(new Runnable() { + @Override + public void run() { + long filelen = file.length(); + Long sliceNumLong = filelen / sliceSize; + if (filelen % sliceSize != 0) { + sliceNumLong++; + } + // TODO Auto-generated method stub + while (true) { + Long progressInteger = upLoadClient.recvCount.get(); + System.out.println("当前已经上传的数据片个数:" + progressInteger); + Double progressPercentDouble=Math.floor((progressInteger*100)/sliceNumLong); + Integer progressPercentInteger=progressPercentDouble.intValue(); + String textString=progressPercentInteger.toString()+"%"; + // 更新进度条 +// String textString=progressInteger.toString()+ "/" + cycleInteger; + if (!text.getText().equals(textString)) { + updateProgress(progressInteger, sliceNumLong); + text.setText(textString); + } + //如果当前进度条 + if (progressInteger.equals(sliceNumLong)) { + text.setText("100%"); + updateProgress(1,1); + System.out.println("***************监听进度条的线程结束监听*************"); + break; + } + try { + Thread.sleep(30); + } catch (InterruptedException e) { + System.out.println(e.getMessage()); + text.setText("100%"); + updateProgress(1,1); + System.out.println("***************监听进度条的线程结束监听*************"); + break; + } + } + } + }); + // 先发送一个开始上传信号 + String serverFilePath = serverpath + MinoaDataAPI.getPureFileNameFromWindows(file.getAbsolutePath()); +// ECOSUtil.sendUploadFileBeginSignal(serverFilePath); + listenThread.start(); + // 上传文件 + boolean res = upLoadClient.uploadData(); + // 上传完成或取消上传后,取消上传进度监听 + listenThread.interrupt(); + Long endTime = System.currentTimeMillis(); + System.out.println("耗时:" + (endTime - startTime) + "ms"); + LoggerUtil.insertFileAction(file.getAbsolutePath(),"上传",(endTime - startTime) + "ms"); + // 每个分片都上传成功 + if (res) { + System.out.println("已成功将文件" + file.getAbsolutePath() + "上传至服务器" + serverpath + "目录下。"); +// String serverFilePath = serverpath + MinoaDataAPI.getPureFileNameFromWindows(file.getAbsolutePath()); +// ECOSUtil.sendUploadFileEndSignal(serverFilePath); +// ECOSUtil.sendECOSUploadEndSignal(serverFilePath); + } else { + System.out.println("由于未知原因,本地文件" + file.getAbsolutePath() + "上传服务器失败。"); + } + // 设置取消按钮状态 + button.setDisable(true); + // 设置确认按钮状态 + sureButton.setDisable(false); + return true; + } + }; + } + + // 文件数据上传TASK:多线程 +// public Task createWorker(File file, String serverpath, Button button, Text text) { +// return new Task() { +// @Override +// protected Object call() throws Exception { +// //获取要上传文件的文件长度,计算上传次数 +// long filelen = file.length(); +// int cycle = (int) (filelen / sliceSize); +// if (filelen % sliceSize != 0) { +// cycle++; +// } +// System.out.println("file-len: " + filelen); +// System.out.println("cycle: " + cycle); +// //发送上传命令给上传接口,接口自行取本地数据 +// int flagSuccess = 0; //记录收到200回复的次数 +// // 多线程上传 +// if (filelen <= sliceSize) { +// System.out.println("只有一个分片,不需要多线程上传"); +// OrderInfo orderInfo = new OrderInfo(); +// ResponseData responseData = new ResponseData(); +// Long seqLong = getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// //此时传入的文件路径是windows下的绝对路径 +//// String testStr = getUploadFile(file.getAbsolutePath(), i * mainApp.sliceSize); +// String testStr = loginerController.getUploadFile(file.getAbsolutePath(), 0); +// System.out.println("getUploadFileJson: " + testStr); +// orderInfo.setJsonString(testStr); +// minoaDataAPI.executeOrder("/uploadFile", orderInfo, serverpath); +// responseData = minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// // System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +// JSONObject jsonObject = responseData.praseRequestData(); +//// responseData.printSelf(); +// // 解析数据 +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// flagSuccess++; +// } +// } else { +// System.out.println("线程数为:" + threadNumberForFileTransfer); +// //多个分片,需要多线程上传 +// UploadFileManager uploadFileManager = new UploadFileManager( +// file.getAbsolutePath(), +// serverpath, threadNumberForFileTransfer, sliceSize, filelen); +// //启动一个线程监听更新的传输进度值 +// Thread listenThread = new Thread(new Runnable() { +// @Override +// public void run() { +// long filelen = file.length(); +// int cycleInteger = (int) (filelen / sliceSize); +// if (filelen % sliceSize != 0) { +// cycleInteger++; +// } +// // TODO Auto-generated method stub +// while (true) { +// Integer progressInteger = uploadFileManager.successUploadedDataSlices.get(); +// System.out.println("当前已经上传的数据片个数:" + progressInteger); +// // 更新进度条 +// updateProgress(progressInteger, cycleInteger); +// if (!text.getText().equals(progressInteger.toString())) { +// text.setText(progressInteger.toString() + "/" + cycleInteger); +// } +// //如果当前进度条 +// if (progressInteger == cycleInteger) { +// System.out.println("***************监听进度条的线程结束监听*************"); +// break; +// } +// } +// } +// }); +// listenThread.start(); +// flagSuccess = uploadFileManager.start(); +// } +// updateProgress(1, 1); +// //每个分片都上传成功 +// if (flagSuccess == cycle) { +//// loginerController.sendTransSuccess(file,serverpath); +// System.out.println("已成功将文件" + file.getAbsolutePath() + "上传至服务器" + serverpath + "目录下。"); +// } else { +// System.out.println("由于未知原因,本地文件" + file.getAbsolutePath() + "上传服务器失败。"); +// } +// //设置确认按钮 +// button.setDisable(false); +// return true; +// } +// }; +// } + +// // 上传本地文件到服务器的共享文件夹下 +// public Integer uploadFileToServerSharedFolder(File file) { +// Integer res = loginerController.sendFileToServer(file, this.sharedFileServerPath); +// return res; +// } + + //请求服务器某文件的字节长度 + public Long getServerFileByteLength(String filename) { + return loginerController.getServerFileLength(filename); + } + + //201大数据接口:取数据片 + public String getGetDataSlice201(String dataId, Integer sliceNo) { + return loginerController.getGetDataSlice(dataId, sliceNo); + } + + // 开启监听之后接收到新聊天消息时的处理=>被minoaDataAPI调用 + public void handleGetNewMessage(SingleMessage singleMessage) { + // 判断联系人列表界面是否加载出来,如果没有加载出来【0xx】,则:将消息加入未读消息列表 + // 否则(联系人列表已加载),判断聊天窗口是否加载出来 + // 如无聊天窗口[10x],则:将消息加入未读消息列表,刷新联系人列表/刷新小红点 + // 如有聊天窗口,判断聊天窗口联系人是否是当前未读消息的发送方 + // 否[110],则:将消息加入未读消息列表,刷新联系人列表/刷新小红点 + // 是[111],则:将消息加到log里,增加消息气泡 + // 非FX线程调用FX的UI组件 + Platform.runLater(() -> { + if ((currentSecondPane.equals(SECOND_MESSAGEPANE)) && (contactListFinished == true)) { + if ((currentMainPane.equals(MAIN_CHATBOXPANE)) && (chatBoxIniFinished == true)) { + if (chatBoxController.getContactName().equals(singleMessage.getFromName())) { + messageLogMap.get(singleMessage.getFromName()).add(singleMessage); + chatBoxController.praseSingleMessageToMessagePane(singleMessage); + } else { + notReadMessageList.addNotReadMessage(singleMessage); + try { + reloadMessageListOverview(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } else { + notReadMessageList.addNotReadMessage(singleMessage); + try { + reloadMessageListOverview(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } else { + notReadMessageList.addNotReadMessage(singleMessage); + } + }); + + } + + // 开启监听之后接收到需要自己报销审批消息时的处理=>被minoaDataAPI调用 + public void handleGetNewReimToSelf() { + // 非FX线程调用FX的UI组件 + Platform.runLater(() -> { + //重载报销【发给自己的】 + if ((currentSecondPane.equals(SECOND_HOMEPANE)) + && (currentMainPane.equals(MAIN_REIMPANE)) + && (currentReimPane.equals(MAIN_REIMPANE_REC))) { + try { + this.reloadReimbursementOverview("R"); + } catch (IOException e) { + e.printStackTrace(); + } + } + if ((currentSecondPane.equals(SECOND_HOMEPANE)) + && (currentMainPane.equals(MAIN_REIMPANE)) + && (currentReimPane.equals(MAIN_REIMPANE_SEND))) { + try { + this.reloadReimbursementOverview("S"); + } catch (IOException e) { + e.printStackTrace(); + } + } else if ((currentSecondPane.equals(SECOND_HOMEPANE)) + && (currentMainPane.equals(MAIN_REIMDEALPANE))) { + //重载报销待审核【发给自己的】 + try { + this.reloadReimbursementDealOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + // 开启监听之后接收到发送给自己的邮件时的处理=>被minoaDataAPI调用 + public void handleGetNewEmailToSelf() { + // 非FX线程调用FX的UI组件 + Platform.runLater(() -> { + //重载邮件收件箱 + if ((currentSecondPane.equals(SECOND_MINEPANE)) + && (currentMainPane.equals(MAIN_EMAILPANE)) + && (currentEmailPane.equals(MAIN_EMAILPANE_REC))) { + try { + this.reloadEmailOverview("R"); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + // 开启监听之后接收到新通知消息时的处理=>被minoaDataAPI调用 + public void handleGetNewNotice(SingleNotice singleNotice) { + //先将该通知加入前端数据; + // 判断“我的”列表界面及通知界面是否加载出来, + //如果都加载出来,就刷新界面,否则不进行刷新 + // 非FX线程调用FX的UI组件 + Platform.runLater(() -> { + if ((currentSecondPane.equals(SECOND_MINEPANE)) && (currentMainPane.equals(MAIN_NOTICEPANE))) { + try { +// this.noticeList.add(singleNotice); + loginerController.reloadNoticeList(); + this.reloadNoticeOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { +// this.noticeList.add(singleNotice); + loginerController.reloadNoticeList(); + } + }); + } + + @Override + public void start(Stage primaryStage) { + //设置关闭监听,确保在关闭stage后关闭子线程 + primaryStage.setOnCloseRequest(new EventHandler() { + @Override + public void handle(WindowEvent event) { + System.exit(0); + } + }); + + this.primaryStage = primaryStage; + // 设置平台自带标题栏的名称 + this.primaryStage.setTitle("FileSystemWindows"); + // 设置平台自带标题栏的图标 + this.primaryStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + // 设置平台stage显示格式 +// this.primaryStage.initStyle(StageStyle.TRANSPARENT);//不带标题栏,透明背景 + this.primaryStage.initStyle(StageStyle.DECORATED);// 带标题栏,默认格式 +// this.primaryStage.initStyle(StageStyle.UNDECORATED);//不带,白色背景 +// this.primaryStage.initStyle(StageStyle.UNIFIED);//带,白色覆盖到表面,weird +// this.primaryStage.initStyle(StageStyle.UTILITY);//带,但是只有一个退出,没有放缩 + this.primaryStage.setResizable(false); + showLoginOverview(); + } + + // 展示登录界面 + public void showLoginOverview() { + try { + // 加载overview + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/login/LoginOverview.fxml")); + AnchorPane loginOverview = (AnchorPane) loader.load(); + + // 设置scene + Scene scene = new Scene(loginOverview); + + // 传参给控制器 + loginerController = loader.getController(); + loginerController.setMainApp(this); + + // 判断是否是登录状态,且账号密码是正确的 + boolean m_flag=false; + boolean isLoginning= KeepLoginStatus.getLogineStatus(); + if(isLoginning==true){ + Loginer m_loginer=KeepLoginStatus.getLoginerInfo(); + if((m_loginer.getUserName()!=null)&&(m_loginer.getPassWord()!=null)){ + System.out.println("login status: "+isLoginning); + System.out.println("loginer name: "+m_loginer.getUserName()); + System.out.println("loginer password: "+m_loginer.getPassWord()); + String loginString=loginerController.getLoginJsonRSAString( + m_loginer.getUserName(),m_loginer.getPassWord()); + if(loginerController.checkPass(loginString)){ + m_flag=true; + } + } + } + + if(m_flag){ + // 是登录状态且账号密码正确 + isNeedKnowLoginStatus=true; + iniShow(); + }else{ + // 否则 + primaryStage.setScene(scene); + } + primaryStage.show(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 使用弹窗展示设置界面 + public void showSettingOverview() { + try { + // 加载overview + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/login/SettingOverview.fxml")); + AnchorPane registerOverview = (AnchorPane) loader.load(); + + // 创建注册Stage + Stage dialogStage = new Stage(); + dialogStage.setTitle("FileSystemWindows-参数配置"); + dialogStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + dialogStage.initStyle(StageStyle.UTILITY);// 带标题栏,无放缩 + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(registerOverview); + dialogStage.setScene(scene); + + // 传参给控制器 + SettingOverviewController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + + // 打开注册Stage + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 使用弹窗展示注册界面 + public void showRegisterOverview() { + try { + // 加载overview + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/login/RegisterOverview.fxml")); + AnchorPane registerOverview = (AnchorPane) loader.load(); + + // 创建注册Stage + Stage dialogStage = new Stage(); + dialogStage.setTitle("FileSystemWindows-用户注册"); + dialogStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + dialogStage.initStyle(StageStyle.UTILITY);// 带标题栏,无放缩 + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(registerOverview); + dialogStage.setScene(scene); + + // 传参给控制器 + RegisterOverviewController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + + // 打开注册Stage + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 使用三维翻转效果展示注册界面 + public void showRegisterOverviewByFlip() { + } + + // 展示登录后的初始化界面 + public void iniShow() { + // 展示导航界面 + showNavigationBarOverview(); + } + + // 展示导航界面 + public void showNavigationBarOverview() { + try { + // 加载overview + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/NavigationBarOverview.fxml")); + navigationBar = (BorderPane) loader.load(); + + // 设置scene + Scene scene = new Scene(navigationBar); + primaryStage.setScene(scene); + + // 传参给控制器 + barController = loader.getController(); + barController.setMainApp(this); + + showMineListOverview(); +// showMessageListOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 使用弹窗展示个人信息界面 + public void showHeadPhotoOverview() { + try { + // 加载overview + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/HeadPhotoAboutMeOverview.fxml")); + AnchorPane headPhotoShowOverview = (AnchorPane) loader.load(); + + // 创建注册Stage + Stage dialogStage = new Stage(); + dialogStage.setTitle("FileSystemWindows-我的信息"); + dialogStage.getIcons().add(new Image("file:resources/images/logo_mini.png")); + dialogStage.initStyle(StageStyle.DECORATED);// 带标题栏,默认格式 + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.setResizable(false); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(headPhotoShowOverview); + dialogStage.setScene(scene); + + // 传参给控制器 + HeadPhotoAboutMeController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + + // 打开注册Stage + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 二级导航栏设置 + // 二级导航栏设为主页 + public void showHomeListOverview() throws IOException { + currentSecondPane = SECOND_NULLPANE; + // 置空主面板 + showNullOverview(); + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/HomeListOverview.fxml")); + // 跳转到相应二级导航栏 + barController.skipListView(loader); + // 设置对应的二级控制器 + HomeListController homeBarController = loader.getController(); + homeBarController.setMainApp(this); + currentSecondPane = SECOND_HOMEPANE; + } + + // 设为报销申请 + public void showReimbursementOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/ReimbursementApplyOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + ReimbursementApplyController reimbursementApplyController = loader.getController(); + reimbursementApplyController.setMainApp(this); + + currentMainPane = MAIN_REIMPANE; + } + + // 重新加载我的报销【我申报的和我审核的】 + public void reloadReimbursementOverview(String controlString) throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/ReimbursementApplyOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + ReimbursementApplyController reimbursementApplyController = loader.getController(); + reimbursementApplyController.setMainApp(this, controlString); + + currentMainPane = MAIN_REIMPANE; + } + + // 展示某条报销的详细信息 + public void showReimbursementDetailsDialog(Reimbursement reimbursement) { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/SingleReimbursementDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("报销"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + SingleReimbursementDetailsController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + controller.setReimInfo(reimbursement); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 审核某条报销 + public void dealReimbursementDetailsDialog(Reimbursement reimbursement) { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/DealSingleReimbursement.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("审核报销"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + DealSingleReimbursementController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + controller.setReimInfo(reimbursement); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 展示编辑报销界面 + public void showWritterReimbursementDialog() { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/WrittenReimbursementDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("报销申请"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + WrittenReimbursementController controller = loader.getController(); + controller.setStage(dialogStage); + controller.setMainApp(this); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 设为报销审核 + public void showReimbursementDealOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/ReimbursementDealOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + ReimbursementDealController controller = loader.getController(); + controller.setMainApp(this); + + currentMainPane = MAIN_REIMDEALPANE; + } + + // 重新加载报销审核界面 + public void reloadReimbursementDealOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/ReimbursementDealOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + ReimbursementDealController controller = loader.getController(); + controller.setMainApp(this); + + currentMainPane = MAIN_REIMDEALPANE; + } + + + // 二级导航栏设为联系人列表页 + public void showMessageListOverview() throws IOException { + currentSecondPane = SECOND_NULLPANE; + showFileShareOverview(); +// contactListFinished = false; +// // 置空主面板 +// showNullOverview(); +// // 加载FXMLLoader +// FXMLLoader loader = new FXMLLoader(); +// loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/message/MessageListOverview.fxml")); +// // 跳转到相应二级导航栏 +// barController.skipListView(loader); +// // 设置对应的二级控制器 +// messageBarController = loader.getController(); +// messageBarController.setMainApp(this); +// contactListFinished = true; + currentSecondPane = SECOND_MESSAGEPANE; + } + + public void reloadMessageListOverview() throws IOException { + currentSecondPane = SECOND_NULLPANE; + contactListFinished = false; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/message/MessageListOverview.fxml")); + // 跳转到相应二级导航栏 + barController.skipListView(loader); + // 设置对应的二级控制器 + messageBarController = loader.getController(); + messageBarController.setMainApp(this); + contactListFinished = true; + currentSecondPane = SECOND_MESSAGEPANE; + } + + // 二级导航栏设为个人信息页 + public void showMineListOverview() throws IOException { + currentSecondPane = SECOND_NULLPANE; +// // 置空主面板 +// showNullOverview(); +// // 加载FXMLLoader +// FXMLLoader loader = new FXMLLoader(); +// loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MineListOverview.fxml")); +// // 跳转到相应二级导航栏 +// barController.skipListView(loader); +// // 设置对应的二级控制器 +// MineListController mineBarController = loader.getController(); +// mineBarController.setMainApp(this); + showCloudFileOverview(); + currentSecondPane = SECOND_MINEPANE; + } + + // 主面板设置{置空} + // 置空 + public void showNullOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/home/NullContextOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + currentMainPane = MAIN_NULLPANE; + } + + // 主面板设置{二级导航栏为主页时} + + // 主面板设置{二级导航栏为聊天页时} + public void showMessageDetails(String personName) { + currentMainPane = MAIN_NULLPANE; + // 消除掉小红点=>1. 获取来自此人的未读消息放入log(仅仅放入not,忽略iniNot) +// 2. 清空数据结构中此人的未读消息(iniNot和not都要清空) +// 3. 重新加载联系人列表界面(消除前端小红点) + // 1.转移未读消息 + List notlist = notReadMessageList.getNotReadMessageByPerson(personName); + for (@SuppressWarnings("rawtypes") + Iterator iterator = notlist.iterator(); iterator.hasNext(); ) { + SingleMessage singleMessage = (SingleMessage) iterator.next(); + messageLogMap.get(singleMessage.getFromName()).add(singleMessage); + } + // 输出测试 +// System.out.println("iniNotReadNumber-before: "+iniNotReadMessageListWhenLogin.getNotReadMessageNumberByPerson(personName)); +// System.out.println("notReadNumber-before: "+notReadMessageList.getNotReadMessageNumberByPerson(personName)); + // 2.清空未读消息 + iniNotReadMessageListWhenLogin.removeNotReadMessageByPerson(personName); + notReadMessageList.removeNotReadMessageByPerson(personName); + // 输出测试 +// System.out.println("iniNotReadNumber-after: "+iniNotReadMessageListWhenLogin.getNotReadMessageNumberByPerson(personName)); +// System.out.println("notReadNumber-after: "+notReadMessageList.getNotReadMessageNumberByPerson(personName)); + // 3.重新加载联系人列表 + try { + reloadMessageListOverview(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 加载聊天窗口 + chatBoxIniFinished = false; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/message/ChatBoxOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 + chatBoxController = loader.getController(); + chatBoxController.setContactName(personName); + chatBoxController.setMainApp(this); + chatBoxIniFinished = true; + currentMainPane = MAIN_CHATBOXPANE; + } + + // 主面板设置{二级导航栏为个人信息页时} + // 设为我的通知 + public void showNoticeOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyNoticeOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 + MyNoticeController myNoticeController = loader.getController(); + myNoticeController.setMainApp(this); + currentMainPane = MAIN_NOTICEPANE; + } + + // 重载我的通知界面 + public void reloadNoticeOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyNoticeOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 + MyNoticeController myNoticeController = loader.getController(); + myNoticeController.setMainApp(this); + currentMainPane = MAIN_NOTICEPANE; + } + + // 展示某条通知的详细信息 + public void showSingleNoticeDialog(SingleNotice singleNotice) { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/SingleNoticeDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("通知"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the notice into the controller. + SingleNoticeDetailsController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setNotice(singleNotice); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 展示编辑通知界面 + public void showWritterNoticeDialog() { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/WrittenNoticeDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("发布通知"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + WrittenNoticeController controller = loader.getController(); + controller.setStage(dialogStage); + controller.setMainApp(this); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 设为我的邮件 + public void showEmailOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyEmailOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + MyEmailController myEmailController = loader.getController(); + myEmailController.setMainApp(this); + + currentMainPane = MAIN_EMAILPANE; + } + + // 重新加载我的邮件【收件箱或者发件箱】 + public void reloadEmailOverview(String controlString) throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyEmailOverview.fxml")); + + // 修改主面板内容 + barController.skipMainContextView(loader); + + // 设置对应的控制器 + MyEmailController myEmailController = loader.getController(); + myEmailController.setMainApp(this, controlString); + + currentMainPane = MAIN_EMAILPANE; + } + + // 展示某条邮件的详细信息 + public void showEmailDetailsDialog(Email email) { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/SingleEmailDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("邮件"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + SingleEmailDetailsController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + controller.setEmailInfo(email); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 展示编辑邮件界面 + public void showWritterEmailDialog() { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/WrittenEmailDetails.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("发送邮件"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the controller. + WrittenEmailController controller = loader.getController(); + controller.setStage(dialogStage); + controller.setMainApp(this); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 设为我的云文件 + public void showCloudFileOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyCloudFileOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 + this.myCloudFileController = loader.getController(); + this.myCloudFileController.setMainApp(this); + currentMainPane = MAIN_CLOUDFILEPANE; + } + + // 设为文件共享界面 + public void showFileShareOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/cloudfile/FileShareOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 + FileShareController fileShareController = loader.getController(); + fileShareController.setMainApp(this); + currentMainPane = MAIN_FILESHAREPANE; + } + + // 重新加载云文件(目的是刷新listview)=>被mycloudfilecontrol调用 + public void reloadCloudFileOverview(String iniCurrentPath) throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/MyCloudFileOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 设置对应的控制器 +// MyCloudFileController myReloadCloudFileController=loader.getController(); + this.myCloudFileController = loader.getController(); + this.myCloudFileController.setMainApp(this, iniCurrentPath); + currentMainPane = MAIN_CLOUDFILEPANE; + } + + // 展示新建文件夹页面=>接收文件夹名称 + public void showNewFolderDialog() { + try { + // Load the fxml file and create a new stage for the popup dialog. + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/NewFolderDialog.fxml")); + AnchorPane page = (AnchorPane) loader.load(); + + // Create the dialog Stage. + Stage dialogStage = new Stage(); + dialogStage.initStyle(StageStyle.TRANSPARENT); + dialogStage.setTitle("新建文件夹"); + dialogStage.initModality(Modality.WINDOW_MODAL); + dialogStage.initOwner(primaryStage); + Scene scene = new Scene(page); + dialogStage.setScene(scene); + + // Set the notice into the controller. + NewFolderDialogController controller = loader.getController(); + controller.setDialogStage(dialogStage); + controller.setMainApp(this); + + // Show the dialog and wait until the user closes it + dialogStage.showAndWait(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 新建文件夹 + public void newFolder(String newFoldername) { + this.myCloudFileController.makeDir(newFoldername); + } + + // 设为关于我的 + public void showAboutMeOverview() throws IOException { + currentMainPane = MAIN_NULLPANE; + // 加载FXMLLoader + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(MainApp.class.getClassLoader().getResource("fxml/mine/AboutMeOverview.fxml")); + // 修改主面板内容 + barController.skipMainContextView(loader); + // 控制器关联 + AboutMeController aboutMeController = loader.getController(); + aboutMeController.setMainApp(this); + currentMainPane = MAIN_ABOUTMINEPANE; + } + + // 获取primaryStage + public Stage getPrimaryStage() { + return primaryStage; + } + +// public static void main(String[] args) { +// launch(args); +// } +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/CongestionWindow.java b/src/main/java/cn/minoa/dataRequestInterface/CongestionWindow.java new file mode 100644 index 0000000..88ff1ff --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/CongestionWindow.java @@ -0,0 +1,206 @@ +package cn.minoa.dataRequestInterface; + +/** + * @author mac + * @description + * 拥塞窗口 + * @date 2020-03-24 10:25 + */ +public class CongestionWindow { + + //拥塞窗口大小 + private Double cwnd; + + //表示当前正在发送的数据,发送一条数据需要加一,收到成功恢复需要减一 + private Double inFlight; + + //拥塞窗口阈值 + private Double thresh; + + //下一次窗口减小的界限 + private Integer nSupress = 0; + + //收到数据包的数量,收到一个数据包进行加一操作 + private Long nIndata = 0l; + + //发送兴趣包的数量,发出一个兴趣包进行加一操作 + private Long nOutInterest = 0l; + + //更新nsupress时的一个比率 + private static Double rate = 0.5d; + + public CongestionWindow(){ + this.cwnd = 4d; + this.thresh = 300d; + this.inFlight = 0d; + } + public CongestionWindow(Double cwnd, Double thresh, Double inFlight){ + this.inFlight = inFlight; + this.cwnd = cwnd; + this.thresh = thresh; + } + + /** + * 当前窗口是否为空 + * @return + */ + public boolean empty(){ + boolean res = false; + synchronized (this){ + if (inFlight == 0){ + res = true; + } + } + return res; + } + + /** + * 当前窗口是否已满 + * @return + */ + public boolean full(){ + boolean res = false; + synchronized (this){ + if(cwnd == inFlight){ + res = true; + } + } + return res; + } + + /** + * 获取当前还能发送多少数据 + * 如果不能发送数据,返回值为0 + * @return + */ + public int getAvailableCwnd(){ + int res = 0; + synchronized (this){ + if(inFlight >= cwnd){ + res = 0; + }else{ + res = (int)(cwnd - inFlight); + } + } + return res; + } + + + /** + * 在onData函数中 + * 收到一个数据包,执行加一操作,需要加锁 + */ + public void nIndataIncrease(){ + synchronized (this){ + nIndata++; + } + } + + /** + * 发出一个兴趣包,执行加一操作,不需要加锁 + */ + public void nOutInterestIncrease(){ + nOutInterest++; + } + + /** + * inFlight加一 + */ + public void inFlightIncrease(){ + synchronized (this){ + this.inFlight++; + } + } + + public void inFlightIncrease(int inFlight){ + synchronized (this){ + this.inFlight += inFlight; + } + } + + /** + * inFliehgt减一 + */ + public void inFlightDecrease(){ + synchronized (this){ + this.inFlight--; + } + } + public void inFlightDecrease(int inFlight){ + synchronized (this){ + this.inFlight -= inFlight; + } + } + + /** + * 增加拥塞窗口的值 + * + */ + public void cwndIncrease(){ + + synchronized (this){ + if(this.cwnd < this.thresh){ + //慢开始算法 + this.cwnd += 1; + /*if(this.cwnd > this.thresh){ + this.thresh = this.cwnd; + }*/ + }else{ + //拥塞避免算法 + this.cwnd += (1.0/this.cwnd); + } + } + System.out.println("-----------------------------------------cwnd:" + cwnd + ", thresh:" + thresh); + } + + /** + * 减少拥塞窗口的值 + * 每当检测到拥塞时,就需要执行此函数 + */ + public void cwndDecrease(){ + synchronized (this){ + if(nIndata >= nSupress){ +// this.thresh = Math.floor(this.cwnd/2); + this.thresh = this.cwnd/2; + this.cwnd = this.thresh; + nSupress = (int)(nOutInterest + rate*(nOutInterest - nIndata)); + } + } + System.out.println("-----------------------------------------cwnd:" + cwnd + ", thresh:" + thresh); + } + + + /** + * 设置拥塞窗口大小 + * @param cwnd + */ + public void setCwnd(Double cwnd){ + synchronized (this){ + this.cwnd = cwnd; + } + } + + /** + * 获得当前拥塞窗口大小 + * @return + */ + public Integer getCwnd(){ + + int res = 0; + synchronized (this){ + res = this.cwnd.intValue(); + } + return res; + + } + + @Override + public String toString() { + return "CongestionWindow {" + + "cwnd=" + cwnd + + ", inFlight=" + inFlight + + ", thresh=" + thresh + + '}'; + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DataCacheQueue.java b/src/main/java/cn/minoa/dataRequestInterface/DataCacheQueue.java new file mode 100644 index 0000000..1cc66b7 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DataCacheQueue.java @@ -0,0 +1,54 @@ +package cn.minoa.dataRequestInterface; +//缓存服务器的回执数据 + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class DataCacheQueue { + //存储数据回执 +// public static volatile Map responseDataMap; + public static volatile ConcurrentHashMap responseDataMap = new ConcurrentHashMap(); + + public DataCacheQueue() { + // TODO Auto-generated constructor stub +// responseDataMap=new TreeMap(); + responseDataMap=new ConcurrentHashMap(); + } + + //放入一个数据 + public static void setResponseData(Long seq,ResponseData rd) { + responseDataMap.put(seq, rd); + } + + //根据seq获取ResponseData + public static ResponseData getResponseDataBySeq(Long seq) { + //等待数据回执 + while(!responseDataMap.containsKey(seq)) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + ResponseData rd=responseDataMap.get(seq); + responseDataMap.remove(seq); + return rd; + } + + //判断是否为空 + public static boolean isEmpty() { + return responseDataMap.isEmpty(); + } + + + public static void showMapData(){ + System.out.println("this is responseDataMap info:"); + for(Map.Entry entry : responseDataMap.entrySet()){ + System.out.println("entry-key:" + entry.getKey() + ", entry-value:"); + entry.getValue().printSelf(); + + } + System.out.println("--------------- end ----------------"); + } +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DataTestMain.java b/src/main/java/cn/minoa/dataRequestInterface/DataTestMain.java new file mode 100644 index 0000000..fae41d2 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DataTestMain.java @@ -0,0 +1,133 @@ +package cn.minoa.dataRequestInterface; + +import org.json.JSONObject; + +import cn.minoa.MainApp; + +public class DataTestMain { + //文件操作命令 + static private String getFileActionJson(String uuid,String cmd) { + JSONObject jsonObject =new JSONObject(); + String commandString="/fileAction"; + String usernameString="wefree"; + String uuidString=uuid; + String cmdString=cmd; + String filename="/"; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("fileCmd", cmdString); + jsonObject.put("filename", filename); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //发送消息 + static private String getSendRTMsgJson(String uuid,String msgTail) { + JSONObject jsonObject =new JSONObject(); + String commandString="/sendRTMsg"; + String fromString="oa_wefree"; + String toString="wefree"; + String uuidString=uuid; + String msgString="hello,wefree. iam oa_wefree. --"+msgTail; + try { + jsonObject.put("command", commandString); + jsonObject.put("from", fromString); + jsonObject.put("to", toString); + jsonObject.put("uuid", uuidString); + jsonObject.put("msg", msgString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //请求通知消息 + static private String getGetInformJson(Integer pageNo,String uuidString) { + JSONObject jsonObject =new JSONObject(); + String commandString="/getInform"; + String usernameString="wefree"; + Integer pageNoInteger=pageNo; + Integer pageSizeInteger=10; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("pageNo", pageNoInteger); + jsonObject.put("pageSize", pageSizeInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + public static void main(String args[]){ + MainApp mainApp=new MainApp(); + MinoaDataAPI minoaDataAPI=new MinoaDataAPI(mainApp); + //test register +// minoaDataAPI.executeOrder("/register", orderInfo); + //test login + OrderInfo loginOrderInfo=new OrderInfo(); + loginOrderInfo.setSeq((long) 0); + loginOrderInfo.setLoginJson(); + minoaDataAPI.executeOrder("/login", loginOrderInfo); + ResponseData responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) 0); + responseData.printSelf(); + String uuidString=responseData.getUuidByPraseLoginData(); + //test getUsers +// minoaDataAPI.executeOrder("/getUsers", orderInfo); + //test sendRTMsg +// minoaDataAPI.executeOrder("/sendRTMsg", orderInfo); + //test getRTMsg +// minoaDataAPI.executeOrder("/getRTMsg", orderInfo); + //test getRTMsgLog +// minoaDataAPI.executeOrder("/getRTMsgLog", orderInfo); + + //制造点假数据出来[wefree=>oa_wefree] +// OrderInfo orderInfo=new OrderInfo(); +// String sendJsonString; +// for(int i=1;i<5;i++) { +// orderInfo.setSeq((long) i); +// sendJsonString=getSendRTMsgJson(uuidString,String.valueOf(i)); +// orderInfo.setJsonString(sendJsonString); +// minoaDataAPI.executeOrder("/sendRTMsg", orderInfo); +// responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) i); +// responseData.printSelf(); +// } + + //测试请求通知信息 +// OrderInfo orderInfo=new OrderInfo(); +// orderInfo.setSeq((long)1); +// orderInfo.setJsonString(getGetInformJson(0,uuidString)); +// minoaDataAPI.executeOrder("/getInform", orderInfo); +// responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) 1); +// responseData.printSelf(); + + //测试云文件上传下载 + OrderInfo orderInfo=new OrderInfo(); + + orderInfo.setSeq((long)1); + orderInfo.setJsonString(getFileActionJson(uuidString, "ls")); + minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) 1); + responseData.printSelf(); + + orderInfo.setSeq((long)2); + orderInfo.setJsonString(getFileActionJson(uuidString, "rm")); + minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) 2); + responseData.printSelf(); + + orderInfo.setSeq((long)3); + orderInfo.setJsonString(getFileActionJson(uuidString, "mkdir")); + minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData=minoaDataAPI.dataCacheQueue.getResponseDataBySeq((long) 3); + responseData.printSelf(); + } +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DownAndUpLoadData.java b/src/main/java/cn/minoa/dataRequestInterface/DownAndUpLoadData.java new file mode 100644 index 0000000..9a8ab6b --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DownAndUpLoadData.java @@ -0,0 +1,420 @@ +package cn.minoa.dataRequestInterface; +// 多线程上传下载:数据收发接口【3/3】 +import cn.minoa.MainApp; +import cn.minoa.util.StringByteLengthUtil; +import net.named_data.jndn.Data; +import net.named_data.jndn.Interest; +import net.named_data.jndn.Name; +import net.named_data.jndn.OnData; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * @author mac + * @description + * @date 2020-03-16 11:16 + */ +public class DownAndUpLoadData { + + /** + * 构造下载文件的json信息 + * @param filename + * @param sliceNo + * @return + */ + public static String getDownloadFileJson(String filename, Integer sliceNo) { + JSONObject jsonObject = new JSONObject(); + String commandString; + if(filename.contains("/")){ + commandString = "/downloadFile"; + }else{ + commandString="/downloadShareFile"; + } + String usernameString = MainApp.loginer.getUserName(); + String uuidString = MainApp.loginer.getUuid(); + String filenameString = filename; + Integer sliceNoInteger = sliceNo; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("sliceNo", sliceNoInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + System.out.println("getDownloadFileJson: "+jsonObject.toString()); + return jsonObject.toString(); + } + + + /** + * 构造上传文件的json字符串 + * @param filename + * @param offset + * @return + */ + public static String getUploadFile(String filename, Long offset) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/uploadFile"; + String usernameString = MainApp.loginer.getUserName(); + String uuidString = MainApp.loginer.getUuid(); + String filenameString = filename; + Long offsetInteger = offset; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("offset", offsetInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +// public static String getUploadFile(String filename, Integer offset) { +// JSONObject jsonObject = new JSONObject(); +// String commandString = "/uploadFile"; +// String usernameString = MainApp.loginer.getUserName(); +// String uuidString = MainApp.loginer.getUuid(); +// String filenameString = filename; +// Integer offsetInteger = offset; +// try { +// jsonObject.put("command", commandString); +// jsonObject.put("username", usernameString); +// jsonObject.put("uuid", uuidString); +// jsonObject.put("filename", filenameString); +// jsonObject.put("offset", offsetInteger); +// } catch (Exception e) { +// // TODO: handle exception +// System.out.println("exception: " + e.getMessage()); +// } +// return jsonObject.toString(); +// } + + +// // 请求文件分片数据=>返回0表示分片数据被成功下载 +// +// /** +// * 请求文件分片数据 +// * @param filename +// * 文件名 +// * @param sliceNo +// * 分片号 +// * @param localDir +// * 文件下载到本地时的文件夹 +// * @param localName +// * 文件下载到本地时的文件名 +// * @return +// */ +// public static Integer downLoadFileBySliceYuhy(String filename, Integer sliceNo, String localDir, String localName) { +//// System.out.println("&&&&&&&&&&downloaddata::downLoadFileBySliceYuhy method"); +// OrderInfo orderInfo = new OrderInfo(); +// Integer flagInteger = 0; +// // 发送数据请求 +// Long seqLong = MainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// +//// System.out.println("orderInfo_id:" + seqLong); +// +// orderInfo.setJsonString(getDownloadFileJson(filename, sliceNo)); +// +//// System.out.println("*****************" + seqLong + ",&&&&&&&&&&&&&&&," + orderInfo.getJsonString()); +// if(filename.contains("/")){ +// DownAndUpLoadData.executeOrderYhy("/downloadFile", orderInfo, localDir, localName); +// }else{ +// DownAndUpLoadData.executeOrderYhy("/downloadShareFile", orderInfo, localDir, localName); +// } +// +// //输出dataCacheQueue中的map的信息 +//// DataCacheQueue.showMapData(); +// +// //获取头像信息 +// ResponseData responseData = DataCacheQueue.getResponseDataBySeq(seqLong); +// +//// System.out.println("size:" + DataCacheQueue.responseDataMap.size()); +// flagInteger = Integer.parseInt(responseData.getResponseJsonDataString()); +// +//// System.out.println("downloadFileSlice-JSON: " + responseData.getResponseJsonDataString()); +// return flagInteger; +// } + +// /** +// * 向服务器发送下载文件请求,并下载文件 +// * @param orderPrefix +// * 下载文件的命令 +// * @param orderInfo +// * @param localDir +// * @param localName +// * @return +// */ +// public static boolean executeOrderYhy(String orderPrefix, OrderInfo orderInfo, String localDir, String localName) { +//// System.out.println("********************" + "downloadData"); +// // 获取json格式的数据请求字符串 +// String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); +//// System.out.println("requestStr:" + requestStr); +// // 命令前缀格式不对的情况下返回false +// if (requestStr.equals("*wrong command string*")) { +// System.out.println(requestStr); +// return false; +// } +// +// // 发送数据兴趣包 +// Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + orderInfo.getSeq() +"/" +localName.substring(localName.length()-1)); +//// System.out.println("uri:" + MinoaDataAPI.globalPrefixString + orderPrefix + "/" + orderInfo.getSeq() + "/" +localName.substring(localName.length()-1)); +// Interest interest = new Interest(name); +// interest.setMustBeFresh(true); +// try { +// // 文件下载 +// interest.setApplicationParameters(new Blob(requestStr)); +// // 密钥“黑盒” +//// MinoaDataAPI.keyChain.sign(interest); +// KeyManager.INSTANCE.getKeyChain().sign(interest); +// expressTheDownloadInterest(orderPrefix, interest, orderInfo,localDir,localName); +// return true; +// } catch (Exception e) { +// System.out.println("exception: " + e.getMessage()); +// e.printStackTrace(); +// return false; +// } +// } + + //下载文件的数据分片到指定路径,并将文件重命名为指定文件名 +// private static void expressTheDownloadInterest(String orderPrefix, Interest interest, OrderInfo orderInfo, String localDir, String localName) { +// // 设置回执前缀 +// ResponseData responseData = new ResponseData(); +// responseData.setPrefix(orderPrefix); +// try { +// // 使用匿名内部类,发送数据请求兴趣包 +// MinoaDataAPI.face.expressInterest(interest, new OnData() { +// @Override +// public void onData(Interest interest, Data data) { +// // 解析文件名、分片编号 +// String filePathString = localDir; +// Integer sliceSizeInteger = MainApp.sliceSize; +// String filenameString = localName; +// Integer sliceNoInteger = null; +// try { +// JSONObject jsonObject = new JSONObject(orderInfo.getJsonString()); +// sliceNoInteger = jsonObject.getInt("sliceNo"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// // 写入本地文件 +// Integer successInfoInteger = 1; +// try { +// System.out.println("开始写入文件数据分片... :" + filePathString + filenameString); +// int contentSize = data.getContent().size(); +// System.out.println("该数据分片长度为:" + contentSize); +// byte[] content = new byte[contentSize]; +// for (int i = 0; i < contentSize; i++) { +// content[i] = data.getContent().buf().get(i); +// } +// +//// RandomAccessFile randomAccessFile = new RandomAccessFile(filePathString + filenameString, "rw"); +//// // 设置数据分片写入到文件的起点位置 +//// randomAccessFile.seek(sliceNoInteger * sliceSizeInteger); +//// // 写入数据分片 +//// randomAccessFile.write(content, 0, contentSize); +//// randomAccessFile.close(); +// +// FileOutputStream fos = new FileOutputStream(filePathString + filenameString,true); +// fos.write(content,0,contentSize); +// fos.flush();//强制刷新出去 +// fos.close(); +// System.out.println("写入文件数据分片完毕:" + filePathString + filenameString); +// successInfoInteger = 0; +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// // 返回成功写入或失败写入消息 +// responseData.setResponseData(successInfoInteger.toString()); +// DataCacheQueue.setResponseData(orderInfo.getSeq(), responseData); +//// System.out.println("getCommandPrefix:" + responseData.getCommandPrefix()); +//// System.out.println("getResponseJsonDataString:" + responseData.getResponseJsonDataString()); +//// System.out.println("map-key:" + orderInfo.getSeq()); +// } +// }); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } + + +// /** +// * 上传文件 +// * @param orderPrefix +// * @param fileAbsolutePath +// * @param offset +// * @param serverpath +// * @return +// */ +// public static boolean uploadOrder(String orderPrefix, String fileAbsolutePath, Integer offset, String serverpath) { +// +// OrderInfo orderInfo = new OrderInfo(); +// Long seqLong = MainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// String testStr = getUploadFile(fileAbsolutePath, offset); +//// System.out.println("*******************:" + testStr + "*******************"); +// orderInfo.setJsonString(testStr); +// +// // 获取json格式的数据请求字符串 +// String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); +// //System.out.println("requestStr: "+requestStr); +// // 命令前缀格式不对的情况下返回false +// if (requestStr.equals("*wrong command string*")) { +// System.out.println(requestStr); +// return false; +// } +// +// // 发送数据兴趣包 +// Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + orderInfo.getSeq()); +// Interest interest = new Interest(name); +// interest.setMustBeFresh(true); +// //System.out.println("interest name: "+name.toString()); +// try { +// expressTheUploadInterest(orderPrefix, interest, orderInfo,serverpath); +// +// ResponseData responseData = DataCacheQueue.getResponseDataBySeq(seqLong); +// //System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +// JSONObject jsonObject = responseData.praseRequestData(); +// +// // 解析数据 +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// return true; +// }else{ +// return false; +// } +// } catch (Exception e) { +// System.out.println("exception: " + e.getMessage()); +// e.printStackTrace(); +// return false; +// } +// +// } +// +// private static void expressTheUploadInterest(String orderPrefix, Interest interest, OrderInfo orderInfo, String serverPath){ +// ResponseData responseData = new ResponseData(); +// responseData.setPrefix(orderPrefix); +// byte[] requestByte = null; // 请求数据 +// // 重写ondata +// try { +// // 解析并修改文件名【window转server】 +// String localFilePath = ""; +// String serverFilePath = ""; +// Integer offsetInteger = 0; +// try { +// // 解析 +// JSONObject jsonObject = new JSONObject(orderInfo.getJsonString()); +// offsetInteger = jsonObject.getInt("offset"); +//// System.out.println("&&&&&&&&&&&&&&&&&&上传第 " + (offsetInteger/7000) + " 片"); +// localFilePath = jsonObject.getString("filename"); +//// serverFilePath = serverpath + getPureFileNameFromWindows(localFilePath); +// serverFilePath = serverPath + MinoaDataAPI.getPureFileNameFromWindows(localFilePath); +// // 修改 +// jsonObject.remove("filename"); +// jsonObject.put("filename", serverFilePath); +// orderInfo.setJsonString(jsonObject.toString()); +// System.out.println("request head str: " + orderInfo.getJsonString()); +// System.out.println("head str length: "+orderInfo.getJsonString().length()); +// System.out.println("head str byte length: "+ StringByteLengthUtil.getByteLength(orderInfo.getJsonString())); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// // 从本地读取数据 +// byte[] content = new byte[10000]; // 文件分片内容 +// try { +// if(offsetInteger<0){ +// // 发送文件上传结束的标志【offset<0】 +// int jsonLen = StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); +// requestByte = new byte[jsonLen]; +// System.arraycopy(orderInfo.getJsonString().getBytes("utf-8"), 0, requestByte, 0, jsonLen); +// }else { +// RandomAccessFile randomAccessFile = new RandomAccessFile(localFilePath, "rw"); +// // 设置文件的读取起点 +// randomAccessFile.seek(offsetInteger); +// // 尝试读取数据 +// int readLen = randomAccessFile.read(content, 0, 7000); +// randomAccessFile.close(); +// System.out.println("file slice readLen == " + readLen); +//// int jsonLen = orderInfo.getJsonString().length(); +// int jsonLen = StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); +// requestByte = new byte[jsonLen + 1 + readLen]; +// System.arraycopy(orderInfo.getJsonString().getBytes("utf-8"), 0, requestByte, 0, jsonLen); +// requestByte[jsonLen] = 0; +// System.arraycopy(content, 0, requestByte, jsonLen + 1, readLen); +// } +//// RandomAccessFile randomAccessFile = new RandomAccessFile(localFilePath, "rw"); +//// // 设置文件的读取起点 +//// randomAccessFile.seek(offsetInteger); +//// // 尝试读取数据 +//// int readLen = randomAccessFile.read(content, 0, 7000); +//// randomAccessFile.close(); +////// System.out.println("file slice readLen == " + readLen); +//// int jsonLen= StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); +//// requestByte = new byte[jsonLen + 1 + readLen]; +//// System.arraycopy(orderInfo.getJsonString().getBytes(), 0, requestByte, 0, jsonLen); +//// requestByte[jsonLen] = 0; +//// System.arraycopy(content, 0, requestByte, jsonLen + 1, readLen); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// // 赋值兴趣包的附带信息,并标志区块链 +//// System.out.println("requestByte-len: " + requestByte.length); +//// System.out.println("requestByte: " + requestByte); +// interest.setApplicationParameters(new Blob(requestByte)); +//// System.out.println("interest.getApplicationParameters().size(): "+interest.getApplicationParameters().size()); +// // 密钥“黑盒” +// try { +//// MinoaDataAPI.keyChain.sign(interest); +// KeyManager.INSTANCE.getKeyChain().sign(interest); +// } catch (PibImpl.Error e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } catch (KeyChain.Error e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } catch (SecurityException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// // 使用匿名内部类,发送数据请求兴趣包 +// MinoaDataAPI.face.expressInterest(interest, new OnData() { +// @Override +// public void onData(Interest interest, Data data) { +// // 返回成功写入或失败写入消息 +// responseData.setResponseData(data.getContent().toString()); +// DataCacheQueue.setResponseData(orderInfo.getSeq(), responseData); +// } +// }); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DownLoadClient.java b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClient.java new file mode 100644 index 0000000..3773ece --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClient.java @@ -0,0 +1,231 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.MainApp; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class DownLoadClient { + + // 下载前缀 + private String orderPrefix = "/downloadFile"; + + // 要下载的文件的服务器路径 + private String filename; + // 下载到的文件路径 + private String localDir; + // 要下载的文件的总数据片个数 + private Integer sliceNumber; + + // 当前排到应该下载的数据片序号 + private Integer currentSliceNo = 0; + + // 滑动窗口控制类 + private CongestionWindow congestionWindow = new CongestionWindow(); + + // 存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + // 收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + public DownLoadClient(String filename, String localDir, Integer sliceNumber) { + this.filename = filename; + this.localDir = localDir; + System.out.println("localDir: "+this.localDir); + this.sliceNumber = sliceNumber; + } + + /** + * 下载数据的主函数 + * + * @return + */ + public boolean downloadData() { + + while (true) { + //判断是否已经上传安成 + if (recvCount.longValue()>=this.sliceNumber) { + break; + } + + // 每一次发送数据 + int cwnd = 0; + // 获取此次可以发送数据的个数 + while ((cwnd = congestionWindow.getAvailableCwnd()) <= 0) { + } + + for (int i = 0; i < cwnd; i++) { + //TODO 首先发送failedmap中的数据 + if (failedSlice.size() > 0) { + //首先发送failedslice中的数据 + Integer failSliceNo = failedSlice.poll(); + System.out.println("发送数据, name: " + filename + ", failSliceNo:" + failSliceNo+ ", failedSlice Size:" + failedSlice.size()); + recvData(failSliceNo); + } else { + if (this.currentSliceNo < this.sliceNumber) { + System.out.println("发送数据, name: " + filename + ",current SliceNo:" + currentSliceNo + ", failedSlice Size:" + failedSlice.size()); + recvData(currentSliceNo); + currentSliceNo++; + } + } + } + + } + + return true; + } + + /** + * 下载一个数据片 + * + * @param sliceNo + */ + private void recvData(Integer sliceNo) { + // 设置JSON请求命令 + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getDownloadFileJson(filename, sliceNo); + System.out.println("*******************DownloadJson:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 发送兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + interest.setInterestLifetimeMilliseconds(5000); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + // 签名 + try { + // 赋值兴趣包的附带信息,并标志区块链 + interest.setApplicationParameters(new Blob(orderInfo.getJsonString())); +// MinoaDataAPI.keyChain.sign(interest); + KeyManager.INSTANCE.getKeyChain().sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String purefilename= MinoaDataAPI.getPureFileName(filename); + DownLoadData downLoadData = new DownLoadData(this.localDir,purefilename, sliceNo); + // 使用tcp发送数据请求兴趣包 + MinoaDataAPI.face.expressInterest(interest, downLoadData,downLoadData,downLoadData); + // 滑动窗口:flight++ + congestionWindow.inFlightIncrease(); + // 滑动窗口:outInterest++ + congestionWindow.nOutInterestIncrease(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + public class DownLoadData implements OnData, OnTimeout, OnNetworkNack { + // 下载到的本地路径 + private String localDir; + //下载到本地后重命名的文件名称 + private String localName; + // 下载到本地路径后的文件名称 + private Integer sliceNo; + // 标记是否拿到网络回复(数据信息/超时信息) + public boolean isFinished = false; + + public DownLoadData(String localDir,String localName, Integer sliceNo) { + this.localDir = localDir; + this.localName = localName; + this.sliceNo = sliceNo; + } + + @Override + public void onData(Interest interest, Data data) { + // 将文件数据片写入本地文件 + try { + System.out.println("开始写入文件数据分片... :" + localDir + localName); + RandomAccessFile randomAccessFile = new RandomAccessFile(localDir + localName, "rw"); + int contentSize = data.getContent().size(); + System.out.println("该数据分片长度为:" + contentSize); + byte[] content = new byte[contentSize]; + for (int i = 0; i < contentSize; i++) { + content[i] = data.getContent().buf().get(i); + } + // 设置数据分片写入到文件的起点位置 + randomAccessFile.seek(sliceNo * MainApp.sliceSize); + // 写入数据分片 + randomAccessFile.write(content, 0, contentSize); + randomAccessFile.close(); + System.out.println("写入文件数据分片完毕:" + localDir + localName); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 收到成功数据的数量++ + recvCount.incrementAndGet(); + // 滑动窗口:inData++ + congestionWindow.nIndataIncrease(); + System.out.println("第" + sliceNo + "片下载成功!"); + + // 处理拥塞:拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if (data.getCongestionMark() > 0) { + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + // 滑动窗口:cwndDecrease() + congestionWindow.cwndDecrease(); + }else{ + // 滑动窗口:cwndIncrease() + congestionWindow.cwndIncrease(); + } + + // 处理完成 + isFinished = true; + } + + // 处理兴趣包超时 + @Override + public void onTimeout(Interest interest) { + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 加入失败队列 + failedSlice.add(sliceNo); + // 打印超时信息 + System.out.println("########## Time out for interest " + interest.getName()+"###########"); + // 处理完成 + isFinished = true; + } + + // 处理收到一个Nack包 + @Override + public void onNetworkNack(Interest interest, NetworkNack networkNack){ + // 打印超时信息 + System.out.println("########## Nack for interest " + interest.getName() + +", Nack: "+networkNack.toString()); + isFinished=true; + } + + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBPRenewal.java b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBPRenewal.java new file mode 100644 index 0000000..06ca191 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBPRenewal.java @@ -0,0 +1,457 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.Main; +import cn.minoa.MainApp; +import cn.minoa.model.BPRInfo; +import cn.minoa.util.Aes; +import cn.minoa.util.FileBPRUtil; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class DownLoadClientBPRenewal { + + // 下载前缀 + private String orderPrefix = "/downloadFile"; + + // 要下载的文件的服务器路径 + private String filename; + // 下载到的文件路径 + private String localDir; + // 要下载的文件的总数据片个数 + private Integer sliceNumber; + + // 当前排到应该下载的数据片序号 + private Integer currentSliceNo = 0; + + // FacePool + private FacePool facePool; + + // 下载到本地的文件对象 + private RandomAccessFile randomAccessFile; + + + // 滑动窗口控制类 + private CongestionWindow congestionWindow = new CongestionWindow(); + + // 存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + // 收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + // 断点续传:此偏移量/序号之前的文件部分已经全部传输成功 + private AtomicLong successPoint=new AtomicLong(0); + // 断点续传:point及其之后的兴趣包,已经传输成功的兴趣包序号 + private Map successList = new ConcurrentHashMap<>(); + // 断点续传:当前文件哈希值 + private String fileHashString; + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:该数据片超时次数 + private Map timeOutNums = new ConcurrentHashMap<>(); + // 超时重传上限数值,某个兴趣包的重传次数超过此数值,则放弃重传,直接返回false + private Integer timeoutCeil=15; + + // 标记该次传输是否关闭 + private boolean isClosed; + + public DownLoadClientBPRenewal(String filename, String localDir, + Integer sliceNumber, Integer facepoolnumber) { + this.filename = filename; + this.localDir = localDir; + System.out.println("localDir: "+this.localDir); + this.sliceNumber = sliceNumber; + this.facePool=new FacePool(facepoolnumber); + this.facePool.start(MinoaDataAPI.minoaServerIpString,MinoaDataAPI.minoaServerPortInteger); + System.out.println("***********FacePool中并发数:"+facepoolnumber+"***********"); + fileHashString=filename+"_"+sliceNumber; + isClosed=false; + } + + /* + * 传入一个刚被后台成功响应的兴趣包序号, + * 判断[point,current]之间的兴趣包序号是否全部被成功响应 + */ + private boolean isAllOK(Long point,Long currentNo){ + for(Long i=point;i<=currentNo;i++){ + if(!successList.containsKey(i)){ + return false; + } + } + return true; + } + + // 移除successList中已经成功发送的部分,减少内存占用 + private void mvSuccessNum(Long fromNo,Long toNo){ + for(Long i=fromNo;i<=toNo;i++){ + successList.remove(i); + } + } + + // 对每个成功收到200应答的兴趣包执行如下程序, + // 以维持断点续传的功能 + private void refreshSuccessPoint(Long sliceNo){ + // 当前断点 + Long point=successPoint.get(); + // 加入successList + successList.put(sliceNo,true); + // 如果当前断点到新收到数据包之间的兴趣包全部成功响应, + // 更新断点,并清理内存 + if(isAllOK(point,sliceNo)){ + successPoint.getAndSet(sliceNo+1); + mvSuccessNum(point,sliceNo); + } + } + + + // 将本次传输的断点信息保存到本地 + public void setPointInfo(){ + String fileName=MinoaDataAPI.getPureFileName(filename); + FileBPRUtil.setUploadBreakPointInfo( + fileHashString,fileName,localDir,successPoint.get()); + System.out.println("保存断点信息成功!!!"); + } + + // 移除已经上传成功的文件断点信息 + private void rmPointInfo(){ + if(FileBPRUtil.isHaveUploadBreakPointInfo(fileHashString)){ + FileBPRUtil.deleteUploadBreakPointInfo(fileHashString); + } + } + + /** + * 判断是否含有超时次数超过阈值的 + * @return + */ + private boolean isTimeOut(){ + for (Map.Entry entry : timeOutNums.entrySet()) { + System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + if(entry.getValue() > timeoutCeil){ + return true; + } + } + return false; + } + + // 由于点击取消传输之后弹窗关闭, + // 该文件类可能会继续发送数据,或有其它继续执行的代码 + // 这里增加一个函数进行关闭操作 + public void closeSelf(){ + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + System.out.println("取消传输时关闭文件报错:"+e.getMessage()); + } + this.facePool.shutdownNow(); + this.isClosed=true; + } + + + /** + * 下载数据的主函数 + * + * @return + */ + public boolean downloadData() { + // 先初始化一个本地文件 + try { + String localName= MinoaDataAPI.getPureFileName(filename); + this.randomAccessFile = new RandomAccessFile(localDir + localName, "rw"); + System.out.println("本地文件打开"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + // 判断是否有断点续传记录 + if(FileBPRUtil.isHaveUploadBreakPointInfo(fileHashString)){ + // 有记录,则判断文件名和服务器地址是否一致 + BPRInfo bprInfo=FileBPRUtil.getUploadBreakPointInfo(fileHashString); + String fileName=MinoaDataAPI.getPureFileName(filename); + if((!(bprInfo==null)) + &&bprInfo.getFileName().equals(fileName) + &&bprInfo.getFileLocator().equals(localDir)){ + // 都一致,则更新recvCount\currentOffset\successPoint + System.out.println("即将开始断点续传..."); + recvCount.getAndSet(bprInfo.getPoint()); + currentSliceNo=(int)recvCount.get(); + successPoint.getAndSet(bprInfo.getPoint()); + }else{ + System.out.println("即将开始文件下载..."); + } + }else { + System.out.println("即将开始文件下载..."); + } + + while (!isClosed) { + //判断是否已经下载完成 + if (recvCount.longValue()>=this.sliceNumber) { + break; + } + + // 判断是否已经被用户终止下载 + if(MainApp.isCancelFileTransferFlag){ + System.out.println("用户已主动中断下载"); + closeSelf(); + // 保存断点信息 + setPointInfo(); + return false; + } + + // 每一次发送数据 + int cwnd = 0; + // 获取此次可以发送数据的个数 + while ((cwnd = congestionWindow.getAvailableCwnd()) <= 0) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < cwnd; i++) { + //TODO 首先发送failedmap中的数据 + if (failedSlice.size() > 0) { + //首先发送failedslice中的数据 + Integer failSliceNo = failedSlice.poll(); + System.out.println("发送数据, name: " + filename + ", failSliceNo:" + failSliceNo+ ", failedSlice Size:" + failedSlice.size()); + recvData(failSliceNo); + } else { + if (this.currentSliceNo < this.sliceNumber) { + System.out.println("发送数据, name: " + filename + ",current SliceNo:" + currentSliceNo + ", failedSlice Size:" + failedSlice.size()); + recvData(currentSliceNo); + currentSliceNo++; + } + } + } + + if(isTimeOut()){ + System.out.println("丢包超过阈值: "+timeoutCeil+" ,文件传输主动中断"); + closeSelf(); + // 保存断点信息 + setPointInfo(); + return false; + } + + } + +// try { +// this.randomAccessFile.close(); +// System.out.println("本地文件关闭"); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// this.facePool.shutdownNow(); + closeSelf(); + // 更新断点信息 + rmPointInfo(); + return true; + } + + /** + * 下载一个数据片 + * + * @param sliceNo + */ + private void recvData(Integer sliceNo) { + facePool.submit(freeface -> { + // 设置JSON请求命令 + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getDownloadFileJson(filename, sliceNo); + System.out.println("*******************DownloadJson:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 发送兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong + +"/" +MainApp.loginer.getUserName()+"/"+(System.currentTimeMillis()/1000+ MainApp.timeStampBias)); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + interest.setInterestLifetimeMilliseconds(5000); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + // 签名 + try { + // 赋值兴趣包的附带信息,并标志区块链 + // AES加密 + String requestStr=null; + try { + requestStr= Aes.Encrypt(orderInfo.getJsonString()); + } catch (Exception e) { + System.out.println("AES加密错误:"+e.getMessage()); + } + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(requestStr))); + KeyManager.INSTANCE.getKeyChain().sign(interest); +// MinoaDataAPI.keyChain.sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String purefilename = MinoaDataAPI.getPureFileName(filename); + DownLoadData downLoadData = new DownLoadData(this.localDir, purefilename, sliceNo); + // 使用tcp发送数据请求兴趣包 + freeface.expressInterest(interest, downLoadData, downLoadData, downLoadData); + // 滑动窗口:flight++ + congestionWindow.inFlightIncrease(); + // 滑动窗口:outInterest++ + congestionWindow.nOutInterestIncrease(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + + public class DownLoadData implements OnData, OnTimeout, OnNetworkNack { + // 下载到的本地路径 + private String localDir; + //下载到本地后重命名的文件名称 + private String localName; + // 下载到本地路径后的文件名称 + private Integer sliceNo; + // 标记是否拿到网络回复(数据信息/超时信息) + public boolean isFinished = false; + + public DownLoadData(String localDir,String localName, Integer sliceNo) { + this.localDir = localDir; + this.localName = localName; + this.sliceNo = sliceNo; + } + + @Override + public void onData(Interest interest, Data data) { + if(!isClosed) { + System.out.println("##### download file receive one data package #####"); + // 将文件数据片写入本地文件 + try { + synchronized (randomAccessFile) { + System.out.println("开始写入文件数据分片... :" + localDir + localName); +// RandomAccessFile randomAccessFile = new RandomAccessFile(localDir + localName, "rw"); +// int contentSize = data.getContent().size(); +// System.out.println("该数据分片长度为:" + contentSize); +// byte[] content = new byte[contentSize]; +// for (int i = 0; i < contentSize; i++) { +// content[i] = data.getContent().buf().get(i); +// } + // 设置数据分片写入到文件的起点位置 + long offset=((long)sliceNo) * ((long)MainApp.sliceSize); + try{ + randomAccessFile.seek(offset); + }catch (IOException e){ + // 说明文件流已经关闭,则不继续执行 + return ; + } + // 写入数据分片 + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + randomAccessFile.write(Aes.pureDecrypt(dataBuf,Aes.getDefaultKey())); + System.out.println("写入文件数据分片完毕:" + localDir + localName); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 收到成功数据的数量++ + recvCount.incrementAndGet(); + // 滑动窗口:inData++ + congestionWindow.nIndataIncrease(); + // 刷新断点 + refreshSuccessPoint((long)sliceNo); + System.out.println("第" + sliceNo + "片下载成功!"); + + // 处理拥塞:拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if (data.getCongestionMark() > 0) { + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + // 滑动窗口:cwndDecrease() + congestionWindow.cwndDecrease(); + } else { + // 滑动窗口:cwndIncrease() + congestionWindow.cwndIncrease(); + } + + if (timeOutNums.containsKey(this.sliceNo)) { + timeOutNums.remove(this.sliceNo); + } + + // 处理完成 + isFinished = true; + } + } + + // 处理兴趣包超时 + @Override + public void onTimeout(Interest interest) { + if(!isClosed) { + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 加入失败队列 + failedSlice.add(sliceNo); + // 打印超时信息 + System.out.println("########## Time out for interest " + interest.getName() + "###########"); + + if (timeOutNums.containsKey(this.sliceNo)) { + int value = timeOutNums.get(this.sliceNo) + 1; + timeOutNums.put(this.sliceNo, value); + } else { + timeOutNums.put(this.sliceNo, 1); + } + // 处理完成 + isFinished = true; + } + } + + // 处理收到一个Nack包 + @Override + public void onNetworkNack(Interest interest, NetworkNack networkNack) { + if (!isClosed) { + congestionWindow.inFlightDecrease(); + failedSlice.add(sliceNo); + if (timeOutNums.containsKey(this.sliceNo)) { + int value = timeOutNums.get(this.sliceNo) + 1; + timeOutNums.put(this.sliceNo, value); + } else { + timeOutNums.put(this.sliceNo, 1); + } + // 打印超时信息 + System.out.println("########## Nack for interest " + interest.getName() + + ", Nack: " + networkNack.toString()); + isFinished = true; + } + } + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBasedFacePool.java b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBasedFacePool.java new file mode 100644 index 0000000..5b8edcb --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DownLoadClientBasedFacePool.java @@ -0,0 +1,360 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.MainApp; +import cn.minoa.util.Aes; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class DownLoadClientBasedFacePool { + + // 下载前缀 + private String orderPrefix = "/downloadFile"; + + // 要下载的文件的服务器路径 + private String filename; + // 下载到的文件路径 + private String localDir; + // 要下载的文件的总数据片个数 + private Integer sliceNumber; + + // 当前排到应该下载的数据片序号 + private Integer currentSliceNo = 0; + + // FacePool + private FacePool facePool; + + // 下载到本地的文件对象 + private RandomAccessFile randomAccessFile; + + + // 滑动窗口控制类 + private CongestionWindow congestionWindow = new CongestionWindow(); + + // 存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + // 收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:该数据片超时次数 + private Map timeOutNums = new ConcurrentHashMap<>(); + // 超时重传上限数值,某个兴趣包的重传次数超过此数值,则放弃重传,直接返回false + private Integer timeoutCeil=15; + + public DownLoadClientBasedFacePool(String filename, String localDir, + Integer sliceNumber,Integer facepoolnumber) { + this.filename = filename; + this.localDir = localDir; + System.out.println("localDir: "+this.localDir); + this.sliceNumber = sliceNumber; + this.facePool=new FacePool(facepoolnumber); + this.facePool.start(MinoaDataAPI.minoaServerIpString,MinoaDataAPI.minoaServerPortInteger); + System.out.println("***********FacePool中并发数:"+facepoolnumber+"***********"); + } + + /** + * 判断是否含有超时次数超过阈值的 + * @return + */ + private boolean isTimeOut(){ + for (Map.Entry entry : timeOutNums.entrySet()) { + System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + if(entry.getValue() > timeoutCeil){ + return true; + } + } + return false; + } + + /** + * 下载数据的主函数 + * + * @return + */ + public boolean downloadData() { + // 先初始化一个本地文件 + try { + String localName= MinoaDataAPI.getPureFileName(filename); + this.randomAccessFile = new RandomAccessFile(localDir + localName, "rw"); + System.out.println("本地文件打开"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + while (true) { + //判断是否已经上传安成 + if (recvCount.longValue()>=this.sliceNumber) { + break; + } + + // 判断是否已经被用户终止上传 + if(MainApp.isCancelFileTransferFlag){ + System.out.println("用户已主动中断下载"); + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return false; + } + + // 每一次发送数据 + int cwnd = 0; + // 获取此次可以发送数据的个数 + while ((cwnd = congestionWindow.getAvailableCwnd()) <= 0) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < cwnd; i++) { + //TODO 首先发送failedmap中的数据 + if (failedSlice.size() > 0) { + //首先发送failedslice中的数据 + Integer failSliceNo = failedSlice.poll(); + System.out.println("发送数据, name: " + filename + ", failSliceNo:" + failSliceNo+ ", failedSlice Size:" + failedSlice.size()); + recvData(failSliceNo); + } else { + if (this.currentSliceNo < this.sliceNumber) { + System.out.println("发送数据, name: " + filename + ",current SliceNo:" + currentSliceNo + ", failedSlice Size:" + failedSlice.size()); + recvData(currentSliceNo); + currentSliceNo++; + } + } + } + + if(isTimeOut()){ + System.out.println("丢包超过阈值: "+timeoutCeil+" ,文件传输主动中断"); + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return false; + } + + } + + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return true; + } + + /** + * 下载一个数据片 + * + * @param sliceNo + */ + private void recvData(Integer sliceNo) { + facePool.submit(freeface -> { + // 设置JSON请求命令 + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getDownloadFileJson(filename, sliceNo); + System.out.println("*******************DownloadJson:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 发送兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong + +"/" +MainApp.loginer.getUserName()+"/"+(System.currentTimeMillis()/1000)); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + interest.setInterestLifetimeMilliseconds(5000); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + // 签名 + try { + // 赋值兴趣包的附带信息,并标志区块链 + // AES加密 + String requestStr=null; + try { + requestStr= Aes.Encrypt(new String(orderInfo.getJsonString())); + } catch (Exception e) { + System.out.println("AES加密错误:"+e.getMessage()); + } + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(requestStr))); + KeyManager.INSTANCE.getKeyChain().sign(interest); +// MinoaDataAPI.keyChain.sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String purefilename = MinoaDataAPI.getPureFileName(filename); + DownLoadData downLoadData = new DownLoadData(this.localDir, purefilename, sliceNo); + // 使用tcp发送数据请求兴趣包 + freeface.expressInterest(interest, downLoadData, downLoadData, downLoadData); + // 滑动窗口:flight++ + congestionWindow.inFlightIncrease(); + // 滑动窗口:outInterest++ + congestionWindow.nOutInterestIncrease(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + + public class DownLoadData implements OnData, OnTimeout, OnNetworkNack { + // 下载到的本地路径 + private String localDir; + //下载到本地后重命名的文件名称 + private String localName; + // 下载到本地路径后的文件名称 + private Integer sliceNo; + // 标记是否拿到网络回复(数据信息/超时信息) + public boolean isFinished = false; + + public DownLoadData(String localDir,String localName, Integer sliceNo) { + this.localDir = localDir; + this.localName = localName; + this.sliceNo = sliceNo; + } + + @Override + public void onData(Interest interest, Data data) { + if(!MainApp.isCancelFileTransferFlag) { + // 将文件数据片写入本地文件 + try { + synchronized (randomAccessFile) { + System.out.println("开始写入文件数据分片... :" + localDir + localName); +// RandomAccessFile randomAccessFile = new RandomAccessFile(localDir + localName, "rw"); +// int contentSize = data.getContent().size(); +// System.out.println("该数据分片长度为:" + contentSize); +// byte[] content = new byte[contentSize]; +// for (int i = 0; i < contentSize; i++) { +// content[i] = data.getContent().buf().get(i); +// } + // 设置数据分片写入到文件的起点位置 + randomAccessFile.seek(sliceNo * MainApp.sliceSize); + // 写入数据分片 + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + Base64.Encoder base64Encoder = Base64.getMimeEncoder(); + String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = Aes.Decrypt(dataContentBase64,"1234567890abcdef") ; + } catch (Exception e) { + e.printStackTrace(); + } + randomAccessFile.write(plainText.getBytes(StandardCharsets.UTF_8)); +// randomAccessFile.write(content, 0, contentSize); +// randomAccessFile.close(); + System.out.println("写入文件数据分片完毕:" + localDir + localName); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 收到成功数据的数量++ + recvCount.incrementAndGet(); + // 滑动窗口:inData++ + congestionWindow.nIndataIncrease(); + System.out.println("第" + sliceNo + "片下载成功!"); + + // 处理拥塞:拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if (data.getCongestionMark() > 0) { + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + // 滑动窗口:cwndDecrease() + congestionWindow.cwndDecrease(); + } else { + // 滑动窗口:cwndIncrease() + congestionWindow.cwndIncrease(); + } + + if (timeOutNums.containsKey(this.sliceNo)) { + timeOutNums.remove(this.sliceNo); + } + + // 处理完成 + isFinished = true; + } + } + + // 处理兴趣包超时 + @Override + public void onTimeout(Interest interest) { + if(!MainApp.isCancelFileTransferFlag) { + // 滑动窗口:flight-- + congestionWindow.inFlightDecrease(); + // 加入失败队列 + failedSlice.add(sliceNo); + // 打印超时信息 + System.out.println("########## Time out for interest " + interest.getName() + "###########"); + + if (timeOutNums.containsKey(this.sliceNo)) { + int value = timeOutNums.get(this.sliceNo) + 1; + timeOutNums.put(this.sliceNo, value); + } else { + timeOutNums.put(this.sliceNo, 1); + } + // 处理完成 + isFinished = true; + } + } + + // 处理收到一个Nack包 + @Override + public void onNetworkNack(Interest interest, NetworkNack networkNack) { + if (!MainApp.isCancelFileTransferFlag) { + congestionWindow.inFlightDecrease(); + failedSlice.add(sliceNo); + if (timeOutNums.containsKey(this.sliceNo)) { + int value = timeOutNums.get(this.sliceNo) + 1; + timeOutNums.put(this.sliceNo, value); + } else { + timeOutNums.put(this.sliceNo, 1); + } + // 打印超时信息 + System.out.println("########## Nack for interest " + interest.getName() + + ", Nack: " + networkNack.toString()); + isFinished = true; + } + } + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/DownloadFileManager.java b/src/main/java/cn/minoa/dataRequestInterface/DownloadFileManager.java new file mode 100644 index 0000000..e8c323d --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/DownloadFileManager.java @@ -0,0 +1,350 @@ +//package cn.minoa.dataRequestInterface; +//// 多线程上传下载:下载控制器【2/3】 +//import cn.minoa.view.login.LoginOverviewController; +// +//import java.io.*; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.CountDownLatch; +// +///** +// * @author mac +// * @description +// * @date 2020-03-12 19:33 +// */ +//public class DownloadFileManager { +// //要下载的文件,格式:服务器路径+文件名 +// private String fileName; +// //文件保存路径 +// private String saveFilePath; +// //保存的文件名 +// private String saveFileName; +// //该文件总分片数 +// private Integer sliceAllNumbers; +// +// //总的下载线程数 +// private Integer threadNumber; +// +// private LoginOverviewController loginOverviewController; +// +// //是否下载开始 +// private Boolean isStarted = false; +// +// private volatile CountDownLatch latch; +// +// +// //用来监视何时合并文件存放thread的list +// private volatile List downloadFileList = new ArrayList(); +// +// public DownloadFileManager(String fileName, int sliceAllNumbers, String saveFilePath, +// String saveFileName,int threadNumber){ +// this.fileName = fileName; +// this.sliceAllNumbers = sliceAllNumbers; +// this.saveFilePath = saveFilePath; +// this.saveFileName = saveFileName; +// this.threadNumber = threadNumber; +//// this.loginOverviewController = loginOverviewController; +// } +// +// /** +// * 最终调用线程下载,本线程中调用分线程 +// */ +// public int action(){ +// return this.start(); +// } +// +// public int start() { +// long startTime = System.currentTimeMillis(); +// +// if(!isStarted){ +// startDownload(); +// isStarted = true; +// } +// try{ +// +// latch.await(); +// mergeFiles(); +// }catch (InterruptedException e){ +// e.printStackTrace(); +// } +// +// long endTime = System.currentTimeMillis(); +// System.out.println("此次下载用时:" + (endTime - startTime) + "ms"); +// return 0; +// } +// +// /** +// * 真实的开启子线程下载文件 +// */ +// public void startDownload(){ +// //根据总分片数,确定每个子线程需要下载的分片 +// int sliceNumEveryThread = this.sliceAllNumbers/threadNumber; +// int beginSliceNum = 0; +// System.out.println("sliceAllNumbers:" + sliceAllNumbers + ", threadNumber:" +// + threadNumber + ", sliceNumEveryThread:" + sliceNumEveryThread + ",beginSliceNum:" + beginSliceNum); +// if(sliceNumEveryThread == 0){ +// //说明总分片数小于8(线程数量),故每个线程下载一个分片即可 +// this.threadNumber = this.sliceAllNumbers; +// this.latch = new CountDownLatch(threadNumber); +// for(int i = 0; i < this.sliceAllNumbers; i++){ +// DownloadThread downloadThread = new DownloadThread(this.fileName, this.saveFilePath, this.saveFileName, +// i, i,i + 1); +// new Thread(downloadThread).start(); +// downloadFileList.add(downloadThread); +// System.out.println("第" + i + "次下载,分片区间是:[" + i + ", " + (i + 1) + ")" ); +// } +// +// }else{ +// this.latch = new CountDownLatch(threadNumber + 1); +// int endSliceNum = 0, i; +// for(i = 0; i < threadNumber; i++){ +// +// /* if(i == (threadNumber - 1)){ +// endSliceNum = this.sliceAllNumbers; +// }else{*/ +// endSliceNum = beginSliceNum + sliceNumEveryThread; +//// } +// +// //每个子线程下载的文件名是 thradID + savedFileName +// DownloadThread downloadThread = new DownloadThread(this.fileName, this.saveFilePath, this.saveFileName, +// i, beginSliceNum,endSliceNum); +// downloadFileList.add(downloadThread); +// new Thread(downloadThread).start(); +// +// System.out.println("第" + i + "次下载,分片区间是:[" + beginSliceNum + ", " + endSliceNum + ")" ); +// beginSliceNum = endSliceNum; +// } +// if(sliceAllNumbers%threadNumber != 0){ +// DownloadThread downloadThread = new DownloadThread(this.fileName, this.saveFilePath, this.saveFileName, +// i, beginSliceNum,sliceAllNumbers); +// System.out.println("第" + (i) + "次下载,分片区间是:[" + beginSliceNum + ", " + sliceAllNumbers + ")" ); +// downloadFileList.add(downloadThread); +// new Thread(downloadThread).start(); +// threadNumber++; +// }else{ +// latch.countDown(); +// } +// +// } +// +// +// } +// +// /** +// * 合并下载好的子文件 +// */ +// public void mergeFiles(){ +// System.out.println("merge files start ......"); +// +// OutputStream out = null; +// try{ +// out = new FileOutputStream(saveFilePath + saveFileName); +// //遍历子线程下载的文件 +// for(int i = 0; i < threadNumber; i++){ +// InputStream in = new FileInputStream(saveFilePath + saveFileName + i); +// +// byte[] bytes = new byte[2048]; +// int read = 0; +// while((read = in.read(bytes)) != -1){ +// out.write(bytes,0, read); +// out.flush(); +// } +// +// if(in != null){ +// in.close(); +// //删除子线程下载的文件 +// boolean isDeleted = new File(saveFilePath + saveFileName + i).delete(); +// if(isDeleted){ +// System.out.println("临时文件已删除!"); +// }else{ +// System.out.println("临时文件删除失败!"); +// } +// } +// +// } +// }catch (Exception e){ +// e.printStackTrace(); +// }finally { +// if(out != null){ +// try{ +// out.close(); +// }catch (IOException e){ +// e.printStackTrace(); +// } +// } +// } +// +// System.out.println("merge files end ......"); +// } +// +// //getter and setter +// public String getFileName() { +// return fileName; +// } +// +// public void setFileName(String fileName) { +// this.fileName = fileName; +// } +// +// public String getSaveFilePath() { +// return saveFilePath; +// } +// +// public void setSaveFilePath(String saveFilePath) { +// this.saveFilePath = saveFilePath; +// } +// +// public String getSaveFileName() { +// return saveFileName; +// } +// +// public void setSaveFileName(String saveFileName) { +// this.saveFileName = saveFileName; +// } +// +// public Integer getSliceAllNumbers() { +// return sliceAllNumbers; +// } +// +// public void setSliceAllNumbers(Integer sliceAllNumbers) { +// this.sliceAllNumbers = sliceAllNumbers; +// } +// +// public Integer getThreadNumber() { +// return threadNumber; +// } +// +// public void setThreadNumber(Integer threadNumber) { +// this.threadNumber = threadNumber; +// } +// +// public List getDownloadFileList() { +// return downloadFileList; +// } +// +// public void setDownloadFileList(List downloadFileList) { +// this.downloadFileList = downloadFileList; +// } +// +// +// public class DownloadThread extends Thread { +// //要下载的文件名,格式为:服务器路径+文件名称 +// private String fileName; +// //文件存储路径 +// private String localPath; +// //本地存储的文件名称 +// private String localFileName; +// +// //线程ID +// private Integer threadId; +// //第一个文件分片 +// private Integer startSliceNumber; +// //最后一个文件分片,但不包含此分片,区间是前开后闭 +// private Integer endSliceNumber; +// +// volatile private boolean isFinish = false; +// +// //下载文件所使用的线程数 +// private Integer threadNumber = 8; +// //当前正在活动的线程数 +// private Integer activeThread; +// +// public DownloadThread(String fileName, String localPath, String localFileName, +// Integer threadId, int startSliceNumber, int endSliceNumber){ +// this.fileName = fileName; +// this.localFileName = localFileName; +// this.localPath = localPath; +// this.threadId = threadId; +// this.startSliceNumber = startSliceNumber; +// this.endSliceNumber = endSliceNumber; +//// this.loginOverviewController = loginOverviewController; +// } +// +// +// @Override +// public void run() { +// int flagInteger = 0; +//// System.out.println( "threadId为:" + threadId + ",下载的分片为:[" + startSliceNumber + "," + endSliceNumber + ")"); +// //下载文件分片信息 +// for (int i = startSliceNumber; i < endSliceNumber; i++) { +//// System.out.println( "threadId为:" + threadId + ",下载的分片为:(" + i + ")"); +// //此线程下载到本地,文件名为 +// flagInteger += DownAndUpLoadData.downLoadFileBySliceYuhy(fileName, i, localPath, localFileName + threadId); +// +// } +// System.out.println("下载分片的消息回执[0表示全部数据分片已经被写入本地],第" + threadId + "个分片:" + flagInteger); +// this.isFinish = true; +// latch.countDown(); +// } +// +// +// //getter and setter +// public String getFileName() { +// return fileName; +// } +// +// public void setFileName(String fileName) { +// this.fileName = fileName; +// } +// +// public String getLocalPath() { +// return localPath; +// } +// +// public void setLocalPath(String localPath) { +// this.localPath = localPath; +// } +// +// public String getLocalFileName() { +// return localFileName; +// } +// +// public void setLocalFileName(String localFileName) { +// this.localFileName = localFileName; +// } +// +// public Integer getThreadId() { +// return threadId; +// } +// +// public void setThreadId(Integer threadId) { +// this.threadId = threadId; +// } +// +// public Integer getStartSliceNumber() { +// return startSliceNumber; +// } +// +// public void setStartSliceNumber(Integer startSliceNumber) { +// this.startSliceNumber = startSliceNumber; +// } +// +// public Integer getEndSliceNumber() { +// return endSliceNumber; +// } +// +// public void setEndSliceNumber(Integer endSliceNumber) { +// this.endSliceNumber = endSliceNumber; +// } +// +// public boolean isFinish() { +// return isFinish; +// } +// +// public void setFinish(boolean finish) { +// isFinish = finish; +// } +// +// +// public Integer getThreadNumber() { +// return threadNumber; +// } +// +// public void setThreadNumber(Integer threadNumber) { +// this.threadNumber = threadNumber; +// } +// } +// +// +// +//} diff --git a/src/main/java/cn/minoa/dataRequestInterface/FacePool.java b/src/main/java/cn/minoa/dataRequestInterface/FacePool.java new file mode 100644 index 0000000..06fb836 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/FacePool.java @@ -0,0 +1,162 @@ +package cn.minoa.dataRequestInterface; + +import net.named_data.jndn.Face; +import net.named_data.jndn.encoding.EncodingException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Face 池,里面可以创建多个face连接,每个face单独一个线程处理 + */ +public class FacePool { + private int poolSize; + + // 采用阻塞队列来记录任务 + private LinkedBlockingQueue faceLinkedBlockingQueue; + + private ArrayList faces; + + private ExecutorService processEventThreadPool; + private ExecutorService faceDealThreadPool; + + public FacePool(int poolSize) { + this.poolSize = poolSize; + faces = new ArrayList<>(); + processEventThreadPool = Executors.newFixedThreadPool(poolSize); + faceDealThreadPool = Executors.newFixedThreadPool(poolSize); + faceLinkedBlockingQueue = new LinkedBlockingQueue<>(); + } + + public FacePool start(String host, int port) { + for (int i = 0; i < poolSize; i++) { + faces.add(new Face(host, port)); + final int finalI = i; + ProcessEventTask processEventTask = new ProcessEventTask(host, port, faces.get(i), i, task -> { + // 发生错误则自动重新创建face,建立连接,然后将processEvent任务提交到线程池当中 + processEventThreadPool.submit(task); + faces.set(finalI, task.getFace()); + }); + processEventThreadPool.submit(processEventTask); + + // 初始时Face池里所有的Face都是可用的 + faceLinkedBlockingQueue.add(faces.get(i)); + } + return this; + } + + public FacePool submit(AsyncUseFaceCallback asyncUseFaceCallback) { + faceDealThreadPool.submit(() -> { + try { + Face face = faceLinkedBlockingQueue.take(); + asyncUseFaceCallback.onFaceAvailable(face); + faceLinkedBlockingQueue.put(face); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + return this; + } + + public void shutdownNow() { + if (!processEventThreadPool.isShutdown()) { + processEventThreadPool.shutdownNow(); + } + if (!faceDealThreadPool.isShutdown()) { + faceDealThreadPool.shutdown(); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////// + ///// Callback + /////////////////////////////////////////////////////////////////////////////////////////// + public interface AsyncUseFaceCallback { + void onFaceAvailable(Face face); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + ///// Tasks + /////////////////////////////////////////////////////////////////////////////////////////// + + static class ProcessEventTask implements Runnable { + private Face face; + private int id; + private String host; + private int port; + private StatusCallback statusCallback; + + public ProcessEventTask(String host, int port, Face face, int id, StatusCallback statusCallback) { + this.face = face; + this.id = id; + this.host = host; + this.port = port; + this.statusCallback = statusCallback; + } + + public Face getFace() { + return face; + } + + public void setFace(Face face) { + this.face = face; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public StatusCallback getStatusCallback() { + return statusCallback; + } + + public void setStatusCallback(StatusCallback statusCallback) { + this.statusCallback = statusCallback; + } + + @Override + public void run() { +// System.out.println("faceId: " + id + "(host: " + host + ", port: " + port + ") -> start"); + try { + while (true) { + face.processEvents(); +// System.out.println("faceId: " + id + " -> processEvent"); + Thread.sleep(1); + } + } catch (IOException | EncodingException | InterruptedException e) { + System.out.println(e.getMessage()); + this.face = new Face(this.host, this.port); + if (this.statusCallback != null) { + this.statusCallback.onError(this); + } + } + } + + interface StatusCallback { + void onError(ProcessEventTask task); + } + } +} \ No newline at end of file diff --git a/src/main/java/cn/minoa/dataRequestInterface/KeyManager.java b/src/main/java/cn/minoa/dataRequestInterface/KeyManager.java new file mode 100644 index 0000000..a31544f --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/KeyManager.java @@ -0,0 +1,96 @@ +package cn.minoa.dataRequestInterface; + +import net.named_data.jndn.Name; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.pib.*; +import net.named_data.jndn.security.tpm.Tpm; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.tpm.TpmBackEndFile; +import net.named_data.jndn.security.v2.CertificateV2; + +import java.io.File; + +public enum KeyManager { + INSTANCE; + private KeyChain keyChain = null; + + KeyManager() { + try { + this.keyChain = getOrBuildKeyChain("/default"); + } catch (KeyChain.Error | PibImpl.Error | Pib.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error error) { + error.printStackTrace(); + } + } + + public KeyManager initKeyChain(String identityName) throws Pib.Error, Tpm.Error, TpmBackEnd.Error, + CertificateV2.Error, KeyChain.Error, PibImpl.Error { + this.keyChain = getOrBuildKeyChain(identityName); + return this; + } + + public CertificateV2 getCertification(String identityName, String keyName) throws Pib.Error, PibImpl.Error, + CertificateV2.Error, Tpm.Error, KeyChain.Error, TpmBackEnd.Error { + return KeyManager.INSTANCE.initKeyChain(identityName) + .getKeyChain() + .getPib() + .getIdentity(new Name(identityName)) + .getKey(new Name(keyName)) + .getDefaultCertificate(); + } + + private KeyChain getOrBuildKeyChain(String identityName) throws + KeyChain.Error, PibImpl.Error, Pib.Error, Tpm.Error, TpmBackEnd.Error, CertificateV2.Error { + KeyChain keyChain = null; + PibSqlite3 pibSqlite3=new PibSqlite3("." + File.separator + "ndn"+ + File.separator+"pid"); + TpmBackEndFile tpmBackEndFile = new TpmBackEndFile("." + File.separator + "ndn"+ + File.separator+"tpm"); + try { + keyChain = new KeyChain(pibSqlite3, tpmBackEndFile); + PibIdentity identity = keyChain.getPib().getIdentity(new Name(identityName)); + keyChain.setDefaultIdentity(identity); + } catch (Pib.Error error) { + error.printStackTrace(); + System.out.println(error.getMessage()); + // 获取本地记录的用户的身份失败,可能是一个新的身份 + PibIdentity identity = keyChain.createIdentityV2(new Name(identityName)); + keyChain.setDefaultIdentity(identity); + pibSqlite3.setDefaultKeyOfIdentity(identity.getName(), identity.getDefaultKey().getName()); + } + return keyChain; + } + + // 判断本地是否有某个标识名对应的证书 + public boolean isInKeyChain(String identityName) throws + KeyChain.Error, PibImpl.Error, Pib.Error, Tpm.Error, TpmBackEnd.Error, CertificateV2.Error { + KeyChain keyChain = null; + PibSqlite3 pibSqlite3=new PibSqlite3("." + File.separator + "ndn"+ + File.separator+"pid"); + TpmBackEndFile tpmBackEndFile = new TpmBackEndFile("." + File.separator + "ndn"+ + File.separator+"tpm"); + try { + keyChain = new KeyChain(pibSqlite3, tpmBackEndFile); + PibIdentity identity = keyChain.getPib().getIdentity(new Name(identityName)); + keyChain.setDefaultIdentity(identity); + return true; + } catch (Pib.Error error) { +// error.printStackTrace(); + System.out.println(error.getMessage()); + System.out.println("本地无该标识名称对应的证书:"+identityName); + return false; + } + } + + public CertificateV2 getCertification(String identityName) throws Pib.Error, PibImpl.Error { + return keyChain.getPib().getIdentity(new Name(identityName)).getDefaultKey().getDefaultCertificate(); + } + + public KeyChain getKeyChain() { + return keyChain; + } + + public void setKeyChain(KeyChain keyChain) { + this.keyChain = keyChain; + } + +} \ No newline at end of file diff --git a/src/main/java/cn/minoa/dataRequestInterface/MinoaDataAPI.java b/src/main/java/cn/minoa/dataRequestInterface/MinoaDataAPI.java new file mode 100644 index 0000000..6e5b54f --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/MinoaDataAPI.java @@ -0,0 +1,773 @@ +package cn.minoa.dataRequestInterface; +// 与服务器进行数据交互 + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import cn.minoa.model.SingleNotice; +import cn.minoa.util.Aes; +import cn.minoa.util.KeyStorageUtil; +import net.named_data.jndn.*; +import net.named_data.jndn.security.pib.Pib; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.security.tpm.Tpm; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.v2.CertificateV2; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.model.SingleMessage; +import net.named_data.jndn.encoding.EncodingException; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibIdentity; +import net.named_data.jndn.security.pib.PibImpl.Error; +import net.named_data.jndn.security.pib.PibSqlite3; +import net.named_data.jndn.security.tpm.TpmBackEndMemory; +import net.named_data.jndn.util.Blob; + +public class MinoaDataAPI { + public static String keyChainNameString = "/default"; + public static String minoaServerIpString; + public static Integer minoaServerPortInteger; + public static String globalPrefixString; +// private String pidDbPathString; + + // 同一兴趣包超时或丢包次数达到上限时的错误信息 + public static String timeoutError = "timeout times reaches the upper limit."; + // 由于连接问题(多次超时/丢包),返回给主线程的错误码 + public static Integer timeoutCodeInteger = 333; + // 同一兴趣包超时或丢包次数达到上限时的错误信息 + public static String nackError = "receive nack."; + // 由于连接问题(多次超时/丢包),返回给主线程的错误码 + public static Integer nackCodeInteger = 334; + // 除文件上下传外的应答codeInteger非200/201时的报错信息 + // 判断是否是200/201,不会导致界面卡死,超时才会 + public static String wrongAnswerError = "answer codeInteger is not 200/201."; + + // public static KeyChain keyChain; + public static Face face; + public volatile DataCacheQueue dataCacheQueue; + private MainApp mainApp; + + // 标志连接是否正常 + private boolean isConnected; + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:该数据片超时次数 + private Map timeOutNums = new ConcurrentHashMap<>(); + // 超时重传上限数值,某个兴趣包的重传次数超过此数值,则放弃重传,直接返回false + private Integer timeoutCeil = 10; + + public MinoaDataAPI(MainApp mainApp) { + // TODO Auto-generated constructor stub + minoaServerIpString = mainApp.minoaServerIpString; + minoaServerPortInteger = mainApp.minoaServerPortInteger; + globalPrefixString = mainApp.globalPrefixString; + isConnected = false; + this.dataCacheQueue = new DataCacheQueue(); + this.mainApp = mainApp; +// pidDbPathString=mainApp.getClientPath()+ File.separator+"ndn"; +// System.out.println("pidDbPathString: "+pidDbPathString); + runFace(minoaServerIpString, minoaServerPortInteger); + // 首先按照"/default"初始化KeyChain + try { + KeyManager.INSTANCE.initKeyChain(keyChainNameString); + } catch (Pib.Error | PibImpl.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + } + // 从本地初始化keyName + String keyString=KeyStorageUtil.getKeyName(); + try { + // 如果本地keyName指向的证书确实在keyChain中,就将该证书取出 + if((keyString!=null)&&(KeyManager.INSTANCE.isInKeyChain(keyString))){ + keyChainNameString=keyString; + System.out.println("keyChainNameString: "+keyChainNameString); + // 再次按照本地证书前缀初始化KeyChain + try { + KeyManager.INSTANCE.initKeyChain(keyChainNameString); + } catch (Pib.Error | Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + } + } + } catch (Pib.Error | PibImpl.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + } +// try { +// // 密钥“黑盒” +// PibSqlite3 pibSqlite3 = new PibSqlite3(pidDbPathString); +// TpmBackEndMemory tpmBackEndMemory = new TpmBackEndMemory(); +// keyChain = new KeyChain(pibSqlite3, tpmBackEndMemory); +// PibIdentity identity = keyChain.getPib().getIdentity(new Name(keyChainNameString)); +// keyChain.setDefaultIdentity(identity); +// +// System.out.println(keyChain.getDefaultCertificateName().toUri()); +// } catch (Exception e) { +// // TODO: handle exception +// System.out.println("exception: " + e.getMessage()); +// } + } + + /** + * 判断某个key对应的兴趣包的超时次数是否超过阈值 + * + * @return + */ + private boolean isInterestTimeOut(Long timeoutMapKey) { + if (timeOutNums.get(timeoutMapKey) > timeoutCeil) { + return true; + } + return false; + } + + // 开辟线程运行face + public boolean runFace(String ipString, Integer portInteger) { + if (isConnected) { + return true; + } + this.face = new Face(ipString, portInteger); + isConnected = true; + Thread thread = new Thread(new Runnable() { + @Override + public void run() { +// System.out.println("face is trying to link..."); + // TODO Auto-generated method stub + try { + while (true) { + face.processEvents(); + // 线程暂时休眠 + Thread.sleep(5); + } + } catch (InterruptedException | EncodingException | IOException e) { + // TODO Auto-generated catch block +// e.printStackTrace(); + System.out.println(e.getMessage()); + isConnected = false; + } + } + }); + thread.start(); + return isConnected; + } + + public boolean runFace(String ipString) { + return this.runFace(ipString, minoaServerPortInteger); + } + + public boolean runFace() { + return this.runFace(minoaServerIpString, minoaServerPortInteger); + } + + // 开辟线程监听新消息/新通知/新审批:作为心跳包存在,五秒一次 + public void bindNews() { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + // TODO Auto-generated method stub + while (true) { + // 监听是否有新消息等,有,则将其加载到数据内存 + bindNewsProdution(); + // 线程暂时休眠 + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + }); + thread.start(); + } + + // 监听函数:作为心跳包存在,不进行任何处理 + public void bindNewsProdution() { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetNewsJson()); + mainApp.minoaDataAPI.executeOrder("/getNews", orderInfo); + // 获取回复消息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + System.out.println(jsonObject.toString()); + // 如果需要查看登录状态且登录失效 + if (mainApp.isNeedKnowLoginStatus&&(codeInteger == 407)) { + // 登录失效 + mainApp.isNeedKnowLoginStatus=false; + mainApp.reLogin(); + } +// String dataString; +// try { +// dataString = jsonObject.getString("data"); +// if (!dataString.equals("")) { +//// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); +// JSONArray dataJsonArray = new JSONArray(dataString); +// String newsCacheString; +// JSONObject newsCacheJsonObject; +// Integer typeCodeInteger; +// String contentString; +// // 遍历每个新消息或者通知或者审批 将其更新到前端内存数据中 +// for (int i = 0; i < dataJsonArray.length(); i++) { +// newsCacheString = dataJsonArray.getString(i); +// newsCacheJsonObject = new JSONObject(newsCacheString); +// typeCodeInteger = newsCacheJsonObject.getInt("typeCode"); +// contentString = newsCacheJsonObject.getString("content"); +// if (typeCodeInteger == 0) { +// // 处理新消息 +// handleNewSingleMessage(contentString); +// } else if (typeCodeInteger == 1) { +// // 处理新通知 +//// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); +// handleNewSingleNotice(contentString); +// } else if (typeCodeInteger == 2) { +// // 处理新审批[报销审批、邮件] +// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); +// handleNewSingleApproval(contentString); +// } +// } +// }else{ +//// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); +// } +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } + } + + //接收到一个新的审批[邮件/报销审批等,除掉通知] + private void handleNewSingleApproval(String singleApprovalString) { + try { + JSONObject singleApprovalJsonObject = new JSONObject(singleApprovalString); + //拿到审批类型 + Integer typeCodeInteger = singleApprovalJsonObject.getInt("typeCode"); + if (typeCodeInteger == 2) { + System.out.println("接收到一条新的报销审批【我是审核人】"); + mainApp.handleGetNewReimToSelf(); + } else if (typeCodeInteger == 3) { + System.out.println("接收到一条新的邮件【我是接收方】"); + mainApp.handleGetNewEmailToSelf(); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + //接收到一个新的通知 + private void handleNewSingleNotice(String singleNoticeString) { + String singleNoticeCacheString = singleNoticeString; + JSONObject singleNoticeCacheJsonObject; + Integer noticeIdInteger; + String usernameCacheString; + String titleCacheString; + String contentCacheString; + String starttimeCacheString; + String endtimeCacheString; + String attachementCacheString; + try { + singleNoticeCacheJsonObject = new JSONObject(singleNoticeCacheString); + //拿到通知ID + noticeIdInteger = singleNoticeCacheJsonObject.getInt("approvalId"); + // 拿到新闻发布人 + usernameCacheString = singleNoticeCacheJsonObject.getString("proposer"); + // 拿到content +// JSONObject detailJsonObject = singleNoticeCacheJsonObject.getJSONObject("detail"); + String detailString = singleNoticeCacheJsonObject.getString("detail"); + System.out.println("通知详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + contentCacheString = detailJsonObject.getString("content"); + // 拿到title + titleCacheString = detailJsonObject.getString("title"); + // 拿到starttime、endtime + starttimeCacheString = detailJsonObject.getString("startTime"); + endtimeCacheString = detailJsonObject.getString("endTime"); + // 拿到attachment + attachementCacheString = detailJsonObject.getString("attachment"); +// System.out.println("user-"+i+": "+usernameCacheString); + // 新建一个notice + SingleNotice singleNotice = new SingleNotice(); + singleNotice.setNoticeId(noticeIdInteger); + singleNotice.setContent(contentCacheString); + singleNotice.setUsername(usernameCacheString); + singleNotice.setTitle(titleCacheString); + singleNotice.setStarttime(starttimeCacheString); + singleNotice.setEndtime(endtimeCacheString); + singleNotice.setAttachment(attachementCacheString); + //判断该通知是否已经渲染到前端 + boolean flagInNoticeList = isInNoticeList(singleNotice.getNoticeId()); + if (!flagInNoticeList) { + mainApp.handleGetNewNotice(singleNotice); + System.out.println("MinoaDataAPI-handleGetNewMessage success: " + singleNoticeString); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + //判断某个通知(的ID)是否已经在前端数据中存在 + boolean isInNoticeList(Integer noticeId) { + boolean flag = false; + for (int i = 0; i < mainApp.noticeList.size(); i++) { + if (mainApp.noticeList.get(i).getNoticeId() == noticeId) { + flag = true; + break; + } + } + return flag; + } + + // 获取是否有新的[消息/通知/审批]产生的json字符串 + private String getGetNewsJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getNews"; + String userNameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("uuid", uuidString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 处理一条接收到的新聊天消息 + private void handleNewSingleMessage(String singleMessageString) { + try { + JSONObject singleMessageJsonObject = new JSONObject(singleMessageString); + String fromString; + JSONObject fromJsonObject; + String fromNameString; + Integer idInteger; + String msgString; + Long timeLong; + // 解析发送方姓名 + fromString = singleMessageJsonObject.getString("from"); + fromJsonObject = new JSONObject(fromString); + fromNameString = fromJsonObject.getString("username"); + // 解析id + idInteger = singleMessageJsonObject.getInt("id"); + // 解析内容 + msgString = singleMessageJsonObject.getString("msg"); + // 解析时间 + timeLong = singleMessageJsonObject.getLong("time"); + // 构建一条消息对象 + SingleMessage singleMessage = new SingleMessage(); + singleMessage.setFromName(fromNameString); + singleMessage.setMsg(msgString); + singleMessage.setTime(timeLong); + singleMessage.setToName(mainApp.loginer.getUserName()); + singleMessage.setMsgId(idInteger); + // 将这条新消息交给mainApp处理 + mainApp.handleGetNewMessage(singleMessage); + System.out.println("MinoaDataAPI-handleGetNewMessage success: " + singleMessageString); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 根据命令前缀获取相应的请求字符串【JSON】 + public static String getRequestStr(String orderPrefix, OrderInfo orderInfo) { + String requestStr; + switch (orderPrefix) { + case "/register": + requestStr = orderInfo.getJsonString(); + break; + case "/login": + requestStr = orderInfo.getJsonString(); + break; + case "/getUsers": + requestStr = orderInfo.getJsonString(); + break; + case "/sendRTMsg": + requestStr = orderInfo.getJsonString(); + break; + case "/getRTMsg": + requestStr = orderInfo.getJsonString(); + break; + case "/getRTMsgLog": + requestStr = orderInfo.getJsonString(); + break; + case "/getInform": + requestStr = orderInfo.getJsonString(); + break; + case "/hasNews": + requestStr = orderInfo.getJsonString(); + break; + case "/getNews": + requestStr = orderInfo.getJsonString(); + break; + case "/fileAction": + requestStr = orderInfo.getJsonString(); + break; + case "/fileInfo": + requestStr = orderInfo.getJsonString(); + break; + case "/fileShareInfo": + requestStr = orderInfo.getJsonString(); + break; + case "/downloadFile": + requestStr = orderInfo.getJsonString(); + break; + case "/downloadShareFile": + requestStr = orderInfo.getJsonString(); + break; + case "/uploadFile": + requestStr = orderInfo.getJsonString(); + break; + case "/sendApproval": + requestStr = orderInfo.getJsonString(); + break; + case "/getApproval": + requestStr = orderInfo.getJsonString(); + break; + case "/getDataSlice": + requestStr = orderInfo.getJsonString(); + break; + case "/dealApproval": + requestStr = orderInfo.getJsonString(); + break; + case "/ecos_begin": + requestStr = orderInfo.getJsonString(); + break; + case "/ecos_end": + requestStr = orderInfo.getJsonString(); + break; + default: + requestStr = "*wrong command string*"; + break; + } + return requestStr; + } + + // 执行数据请求语句 + public boolean executeOrder(String orderPrefix, OrderInfo orderInfo) { + // 获取json格式的数据请求字符串 + String requestStr = getRequestStr(orderPrefix, orderInfo); + System.out.println("requestStr: "+requestStr); + // 命令前缀格式不对的情况下返回false + if (requestStr.equals("*wrong command string*")) { + System.out.println(requestStr); + return false; + } + + // AES加密 + byte[] requestByte=null; + if(orderPrefix.equals("/register")||orderPrefix.equals("/login")){ + }else{ + try { + requestByte= Aes.newEncrypt(requestStr,Aes.getDefaultKey()); +// requestStr=Aes.Encrypt(requestStr); + } catch (Exception e) { + System.out.println("AES加密错误:"+e.getMessage()); + } + } + + // 发送数据兴趣包 + Name name = new Name(globalPrefixString + orderPrefix+"/"+orderInfo.getSeq() + +"/" +mainApp.loginer.getUserName()+"/"+(System.currentTimeMillis()/1000+MainApp.timeStampBias)); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + if(orderPrefix.equals("/register")||orderPrefix.equals("/login")){ + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(requestStr))); + }else{ + interest.setApplicationParameters(new Blob(requestByte)); + } + interest.setInterestLifetimeMilliseconds(2000); + try { +// keyChain.sign(interest); + // 使用KeyManager管理兴趣包签名 + KeyManager.INSTANCE.getKeyChain().sign(interest); + this.expressTheInterest(orderPrefix, interest, orderInfo.getSeq()); + return true; + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + return false; + } + } + + // 提供特殊接口,执行数据请求语句:为ECOS信号服务 + public boolean executeOrder(String orderPrefix,Interest interest,Long seq) { + try { + this.expressTheInterest(orderPrefix, interest, seq); + return true; + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + return false; + } + } + + // 提供特殊接口,执行时间戳同步语句 + public boolean executeOrder(String orderPrefix,OrderInfo orderInfo,boolean isTimeSync) { + // 发送数据兴趣包 + Name name = new Name(globalPrefixString + orderPrefix+"/"+orderInfo.getSeq()); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + System.out.println("time sync: "+orderInfo.getJsonString()); + interest.setApplicationParameters(new Blob(orderInfo.getJsonString())); + interest.setInterestLifetimeMilliseconds(2000); + try { +// keyChain.sign(interest); + // 使用KeyManager管理兴趣包签名 + KeyManager.INSTANCE.getKeyChain().sign(interest); + this.expressTheInterest(orderPrefix, interest, orderInfo.getSeq()); + return true; + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + return false; + } + } + + + private void expressTheInterest(String orderPrefix, Interest interest, Long dataReqId) { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + HandleInterestTimeOutAndNack handleInterestTimeOutAndNack = + new HandleInterestTimeOutAndNack(orderPrefix, interest, dataReqId); + try { + // 使用匿名内部类,发送数据请求兴趣包 + if (orderPrefix.equals("/getDataSlice")) { + //接收数据块 + face.expressInterest(interest, new OnData() { + @Override + public void onData(Interest interest, Data data) { +// int contentSize = data.getContent().size(); +// System.out.println("该大数据传输的数据块分片长度为:" + contentSize); +// byte[] content = new byte[contentSize]; +// for (int i = 0; i < contentSize; i++) { +// content[i] = data.getContent().buf().get(i); +// System.out.print(i + ": " + content[i] + " "); +// } +// System.out.println("notice content: " + content); + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; +// Base64.Encoder base64Encoder = Base64.getMimeEncoder(); +// String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = Aes.newDecrypt(dataBuf,Aes.getDefaultKey()) ; + } catch (Exception e) { + e.printStackTrace(); + } + responseData.setFileSlice(plainText.getBytes(StandardCharsets.UTF_8)); + dataCacheQueue.setResponseData(dataReqId, responseData); + // 重置其超时次数记录 + if (timeOutNums.containsKey(dataReqId)) { + timeOutNums.remove(dataReqId); + } + } + }, handleInterestTimeOutAndNack, handleInterestTimeOutAndNack); + } else if(orderPrefix.equals("/timeSync")){ + face.expressInterest(interest, new OnData() { + @Override + public void onData(Interest interest, Data data) { + byte[] dataBuf = new byte[data.getContent().size()]; + data.getContent().buf().get(dataBuf, 0, data.getContent().size()); + responseData.setResponseData(data.getContent().toString()); + dataCacheQueue.setResponseData(dataReqId, responseData); + // 重置其超时次数记录 + if (timeOutNums.containsKey(dataReqId)) { + timeOutNums.remove(dataReqId); + } + } + },handleInterestTimeOutAndNack, handleInterestTimeOutAndNack); + + }else { + face.expressInterest(interest, new OnData() { + @Override + public void onData(Interest interest, Data data) { +// System.out.println("on data success"); + // TODO Auto-generated method stub +// System.out.println("ondata interest: "+interest.getName()); +// System.out.println("ondata data: "+data.getContent().toString()); + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; +// Base64.Encoder base64Encoder = Base64.getMimeEncoder(); +// String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = Aes.newDecrypt(dataBuf,Aes.getDefaultKey()) ; + } catch (Exception e) { + e.printStackTrace(); + } + responseData.setResponseData(plainText); + dataCacheQueue.setResponseData(dataReqId, responseData); + // 重置其超时次数记录 + if (timeOutNums.containsKey(dataReqId)) { + timeOutNums.remove(dataReqId); + } + } + }, handleInterestTimeOutAndNack, handleInterestTimeOutAndNack); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 写头像 +// public Integer writeHeadPhotoIntoLocalFile(String filenameString, +// Integer sliceNo,Integer sliceSize,byte[] bytes) { +// String pathString=mainApp.fileLocalPath; +// return writeIntoLocalFile(pathString, filenameString, sliceNo, sliceSize, bytes); +// } + + // 根据包含路径的文件名获取纯文件名[linux下] + public static String getPureFileName(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '/') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[linux]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[linux]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } + + // 根据包含路径的文件名获取纯文件名[windows本地] + public static String getPureFileNameFromWindows(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '\\') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[windows]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[windows]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } +// // 向指定本地文件写入指定数据分片=》被ondata调用 +// public Integer writeIntoLocalFile(String pathString,String filenameString, +// Integer sliceNo,Integer sliceSize,byte[] bytes) { +// try { +// @SuppressWarnings("resource") +// RandomAccessFile randomAccessFile=new RandomAccessFile(pathString+filenameString, "rw"); +// int contentSize= +// randomAccessFile.write(bytes,sliceNo*sliceSize,bytes.length); +// return 0; +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// return 1; +// } +// } + + // 关闭数据连接 + public void closeDataConn() { + this.face.shutdown(); + } + + // 解决处理兴趣包超时之后的重传问题[除上传下载外的兴趣包] + public class HandleInterestTimeOutAndNack implements OnTimeout, OnNetworkNack { + // 命令前缀 + private String orderPrefix; + // 兴趣包 + private Interest interest; + // 请求序列号 + private Long dataReqId; + // 标记是否拿到网络回复(数据信息/超时信息):测试使用 + public boolean isFinished = false; + + public HandleInterestTimeOutAndNack(String orderPrefix, Interest interest, Long dataReqId) { + this.orderPrefix = orderPrefix; + this.interest = interest; + this.interest.setInterestLifetimeMilliseconds(2000); + this.dataReqId = dataReqId; + } + + // 处理兴趣包超时 + @Override + public void onTimeout(Interest interest) { + // 打印超时信息 + System.out.println("########## Time out for interest " + interest.getName() + "###########"); + // 记录此次丢包/超时 + if (timeOutNums.containsKey(this.dataReqId)) { + int value = timeOutNums.get(this.dataReqId) + 1; + timeOutNums.put(this.dataReqId, value); + } else { + timeOutNums.put(this.dataReqId, 1); + } + // 根据是否多次超时,决定是否重传该兴趣包:暂时是全部重传 + // 后期可以改作,超时次数超过阈值时候, + // 自拟定一个指定值放入数据回复缓存区,前端控制类收到该值,就 + // 判定网络极其拥塞,进行弹窗提示。 + if (!isInterestTimeOut(this.dataReqId)) { + // 超时次数没有超过阈值:重传该兴趣包 + expressTheInterest(this.orderPrefix, this.interest, this.dataReqId); + } else { + // 超时次数超过阈值 + System.out.println("该兴趣包的超时次数超过阈值:" + timeoutCeil + "!!!"); + // 返回一个timeout信息放入datacache + ResponseData responseData = new ResponseData(); + responseData.setPrefix(this.orderPrefix); + responseData.setResponseData(getWrongAnswerJson(timeoutCodeInteger, timeoutError)); + dataCacheQueue.setResponseData(this.dataReqId, responseData); + } + // 打印重传信息 + System.out.println("########## Resend the interest " + interest.getName() + "###########"); + // 处理完成 + isFinished = true; + } + + // 处理收到一个Nack包 + @Override + public void onNetworkNack(Interest interest, NetworkNack networkNack) { + // 打印超时信息 + System.out.println("########## Nack for interest " + interest.getName() + + ", Nack: " + networkNack.toString()); + // 返回一个nack信息放入datacache + ResponseData responseData = new ResponseData(); + responseData.setPrefix(this.orderPrefix); + responseData.setResponseData(getWrongAnswerJson(nackCodeInteger, nackError)); + dataCacheQueue.setResponseData(this.dataReqId,responseData); + isFinished = true; + } + } + + // 构造一个json,给前端返回一个可以进行JSON解析的错误信息 + // 用以避免界面卡死 + public static String getWrongAnswerJson(Integer wrongAnswerCode, String wrongAnswerString) { + JSONObject jsonObject = new JSONObject(); + Integer code = wrongAnswerCode; + String errMsg = wrongAnswerString; + String data = ""; + try { + jsonObject.put("code", code); + jsonObject.put("errMsg", errMsg); + jsonObject.put("data", data); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/OrderInfo.java b/src/main/java/cn/minoa/dataRequestInterface/OrderInfo.java new file mode 100644 index 0000000..d2bdc53 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/OrderInfo.java @@ -0,0 +1,85 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.MainApp; +import org.json.JSONObject; + +//请求数据时的附加请求信息 +public class OrderInfo { + //唯一标识某次数据请求 + private Long seqLong; + //数据请求时附加的请求信息 + private String jsonString; + + //数据请求时附加的请求信息 + private byte[] jsonByte; + + //该登录属性用于早期单独的数据测试 + @SuppressWarnings("unused") + private String loginJsonString; + + public OrderInfo() { + // TODO Auto-generated constructor stub + } + + public void setSeq(Long seqLong) { + this.seqLong=seqLong; + + // 查看是否连接正常,否,重新尝试连接; + // 重新连接失败,默认是ip有问题,返回一个错误包 + if(!MainApp.isFaceLinked()){ + System.out.println("!!!face link error!!!"); + ResponseData responseData=new ResponseData(); + String wrongAnswer=MinoaDataAPI.getWrongAnswerJson(336, + "face link error"); + responseData.setPrefix("/wrongAnswer"); + responseData.setResponseData(wrongAnswer); + MainApp.minoaDataAPI.dataCacheQueue.setResponseData(seqLong,responseData); + } + } + + public Long getSeq() { + return this.seqLong; + } + + public void setJsonString(String jsonString){ + this.jsonString=jsonString; + } + + public String getJsonString() { + return this.jsonString; + } + + public void setJsonByte(byte[] jsonByte){ + this.jsonByte=jsonByte; + } + + public byte[] getJsonByte(){ + return this.jsonByte; + } + +// //登录单独列出,方便测试 +// public void setLoginJson(String loginJsonString){ +// this.loginJsonString=loginJsonString; +// } +// public String getLoginJson() { +// return this.loginJsonString; +// } + + +// 产生模拟登录数据 + public void setLoginJson(){ + JSONObject jsonObject = new JSONObject() ; + String commandString="/login"; + String userNameString="wefree"; + String passWordString="12345678"; + try { + jsonObject.put("command",commandString); + jsonObject.put("username",userNameString); + jsonObject.put("password",passWordString); + }catch (Exception e){ + System.out.println("exception: " + e.getMessage()); + } + this.loginJsonString=jsonObject.toString() ; + } + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/ResponseData.java b/src/main/java/cn/minoa/dataRequestInterface/ResponseData.java new file mode 100644 index 0000000..55e551c --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/ResponseData.java @@ -0,0 +1,114 @@ +package cn.minoa.dataRequestInterface; +//负责存储和解析回执数据 + +import org.json.JSONException; +import org.json.JSONObject; + +public class ResponseData{ + //发送请求前初始化此属性 + private String commandPrefix; + //onData()接收到数据赋值此属性 + private String responseJsonDataString; + //接收大数据块的分片数据时候的存储对象=>没有用到。。。 + private byte[] fileSlice; + + public ResponseData() { + // TODO Auto-generated constructor stub + } + + public ResponseData(String commandPrefix,String responseData) { + // TODO Auto-generated constructor stub + this.commandPrefix=commandPrefix; + this.responseJsonDataString=responseData; + } + + public void setPrefix(String commandPrefix) { + this.commandPrefix=commandPrefix; + } + + public void setResponseData(String responseData) { + this.responseJsonDataString=responseData; + } + + public void setFileSlice(byte[] fileSlice) { + System.out.println("ResponseData fileSlice: "+fileSlice.length); + this.fileSlice=new byte[fileSlice.length]; + for(int i=0;i制作假数据用 + public String getUuidByPraseLoginData() { + JSONObject jsonObject = null; + String uuid = null; + try { + jsonObject = new JSONObject(responseJsonDataString); + } catch (JSONException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(codeInteger==200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + JSONObject dataJsonObject=new JSONObject(dataString); + uuid = dataJsonObject.getString("uuid"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return uuid; + } + +} \ No newline at end of file diff --git a/src/main/java/cn/minoa/dataRequestInterface/UpLoadClient.java b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClient.java new file mode 100644 index 0000000..3c8ba95 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClient.java @@ -0,0 +1,335 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.MainApp; +import cn.minoa.util.StringByteLengthUtil; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class UpLoadClient { + + private String orderPrefix = "/uploadFile"; + + private String serverPath; + + private File file; + //总数据片大小 + private Long sliceNumbers; + + private Long currentOffset = 0L; + + private CongestionWindow congestionWindow = new CongestionWindow(); + + //发送失败,需要重新发送的数据。key:第几个数据片,从0开始计数,value:文件偏移 +// private Map failedSlice = new ConcurrentHashMap<>(); + //存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:是否发送成功 + private Map sendRes = new ConcurrentHashMap<>(); + + //收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + + + public UpLoadClient(){ + + } + + public UpLoadClient(File file, String serverPath){ + this.file = file; + this.serverPath = serverPath; + } + + /** + * 判断上一次发送的数据,是否全部成功发送 + * @return + */ +// public boolean isSuccessful(){ +// boolean res = true; +// for(Map.Entry entry : sendRes.entrySet()){ +// if(!entry.getValue()){ +// failedSlice.put(entry.getKey(),entry.getKey()*MainApp.sliceSize); +// res = false; +// } +// System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); +// sendRes.remove(entry.getKey()); +// } +// return res; +// } + + /** + * 上传数据的主函数 + * @return + */ + public boolean uploadData(){ + + + //开启发送数据的线程,具体的发送数据从CongestWindow中取得 + //new Thread(new SendData()).start(); + + if(file.length()% MainApp.sliceSize == 0){ + sliceNumbers = file.length()/ MainApp.sliceSize; + }else{ + sliceNumbers = file.length()/ MainApp.sliceSize + 1; + } + + while(true){ + //判断是否已经上传安成 + if(sliceNumbers <= recvCount.longValue()){ + break; + } + + //每一次发送数据 + int cwnd = 0; + //获取此次可以发送数据的个数 + while((cwnd = congestionWindow.getAvailableCwnd()) <= 0){ + + } + + for(int i=0; i 0){ + //首先发送failedslice中的数据 + Long failOffset = failedSlice.poll(); + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(failOffset); + }else{ + if(currentOffset < file.length()){ + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(currentOffset); + currentOffset += MainApp.sliceSize; + } + } + congestionWindow.inFlightIncrease(); + congestionWindow.nOutInterestIncrease(); + + } + + } + + + return true; + } + + /** + * 发送一个数据片 + * @param offset + */ + private void sendData(Long offset){ + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getUploadFile(file.getAbsolutePath(), offset); + //System.out.println("*******************:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 获取json格式的数据请求字符串 + //String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); + + // 发送数据兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + byte[] requestByte = null; // 请求数据 + // 重写ondata + try { + // 解析并修改文件名【window转server】 + String localFilePath = ""; + String serverFilePath = ""; + try { + // 解析 + JSONObject jsonObject = new JSONObject(orderInfo.getJsonString()); + localFilePath = jsonObject.getString("filename"); +// serverFilePath = serverpath + getPureFileNameFromWindows(localFilePath); + serverFilePath = serverPath + MinoaDataAPI.getPureFileNameFromWindows(localFilePath); + // 修改 + jsonObject.remove("filename"); + jsonObject.put("filename", serverFilePath); + orderInfo.setJsonString(jsonObject.toString()); + System.out.println("request head str: " + orderInfo.getJsonString()); + System.out.println("head str length: "+orderInfo.getJsonString().length()); + System.out.println("head str byte length: "+ StringByteLengthUtil.getByteLength(orderInfo.getJsonString())); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 从本地读取数据 + byte[] content = new byte[10000]; // 文件分片内容 + try { + RandomAccessFile randomAccessFile = new RandomAccessFile(localFilePath, "rw"); + // 设置文件的读取起点 + randomAccessFile.seek(offset); + // 尝试读取数据 + int readLen = randomAccessFile.read(content, 0, 7000); + int jsonLen= StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); + requestByte = new byte[jsonLen + 1 + readLen]; + System.arraycopy(orderInfo.getJsonString().getBytes("utf-8"), 0, requestByte, 0, jsonLen); + requestByte[jsonLen] = 0; + System.arraycopy(content, 0, requestByte, jsonLen + 1, readLen); + randomAccessFile.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 赋值兴趣包的附带信息,并标志区块链 + interest.setApplicationParameters(new Blob(requestByte)); + //超时 + interest.setInterestLifetimeMilliseconds(5000); + // 密钥“黑盒” + try { +// MinoaDataAPI.keyChain.sign(interest); + KeyManager.INSTANCE.getKeyChain().sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + UpLoadData upLoadData = new UpLoadData(orderInfo.getSeq(),responseData, offset/ MainApp.sliceSize); + UpLoadTimeout upLoadTimeout = new UpLoadTimeout(upLoadData); + UpLoadOnNetWorkNack upLoadOnNetWorkNack = new UpLoadOnNetWorkNack(upLoadData); + // 使用tcp发送数据请求兴趣包 +// MinoaDataAPI.runFace(); + MinoaDataAPI.face.expressInterest(interest, upLoadData, upLoadTimeout, upLoadOnNetWorkNack); + + + } catch (IOException e) { + e.printStackTrace(); + } + + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + e.printStackTrace(); + } + } + + + public class UpLoadData implements OnData { + + private Long seq; + private ResponseData responseData; + //第几个数据片 + public Long sliceNum; + + public boolean isFinished = false; + + public UpLoadData(Long seq, ResponseData responseData, Long sliceNum){ + this.seq = seq; + this.responseData = responseData; + this.sliceNum = sliceNum; + } + + @Override + public void onData(Interest interest, Data data){ + responseData.setResponseData(data.getContent().toString()); + JSONObject jsonObject = responseData.praseRequestData(); + + congestionWindow.inFlightDecrease(); + // 解析数据,是否成功上传 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + recvCount.incrementAndGet(); + + congestionWindow.cwndIncrease(); + System.out.println("第" + sliceNum + "片上传成功!"); + + }else{ + + failedSlice.add(sliceNum* MainApp.sliceSize); + System.out.println("第" + sliceNum + "片上传失败!"); + } + //拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if(data.getCongestionMark() > 0){ + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + congestionWindow.cwndDecrease(); + }else{ + //如果没有检测到拥塞,则增加窗口 + congestionWindow.nIndataIncrease(); + } + + isFinished = true; + + } + public int callbackCount = 0 ; + + } + + public class UpLoadTimeout implements OnTimeout { + + + + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadTimeout(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onTimeout(Interest interest){ + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum* MainApp.sliceSize); + callbackCount ++ ; + System.out.println("Time out for interest " + interest.getName()); + upLoadData.isFinished = true; + + } + } + + public class UpLoadOnNetWorkNack implements OnNetworkNack { + + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadOnNetWorkNack(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onNetworkNack(Interest var1, NetworkNack var2){ + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum* MainApp.sliceSize); + callbackCount ++ ; + System.out.println("onNetworkNack " + var1.getName()); + upLoadData.isFinished = true; + } + + } + + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBPRenewal.java b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBPRenewal.java new file mode 100644 index 0000000..f3b4a67 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBPRenewal.java @@ -0,0 +1,534 @@ +package cn.minoa.dataRequestInterface; +// 在原来基于FacePool上传文件的基础上, +// 增加断点续传功能 +import cn.minoa.MainApp; +import cn.minoa.model.BPRInfo; +import cn.minoa.util.Aes; +import cn.minoa.util.EncryptionUtil; +import cn.minoa.util.FileBPRUtil; +import cn.minoa.util.StringByteLengthUtil; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class UpLoadClientBPRenewal { + + private String orderPrefix = "/uploadFile"; + + private String serverPath; + + private File file; + //总数据片大小 + private Long sliceNumbers; + + private Long currentOffset = 0L; + + private CongestionWindow congestionWindow = new CongestionWindow(); + + private FacePool facePool; + + // 要上传的本地文件对象 + private RandomAccessFile randomAccessFile; + + //发送失败,需要重新发送的数据。key:第几个数据片,从0开始计数,value:文件偏移 +// private Map failedSlice = new ConcurrentHashMap<>(); + //存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:是否发送成功 +// private Map sendRes = new ConcurrentHashMap<>(); + + //收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + // 断点续传:此偏移量/序号之前的文件部分已经全部传输成功 + private AtomicLong successPoint=new AtomicLong(0); + // 断点续传:point及其之后的兴趣包,已经传输成功的兴趣包序号 + private Map successList = new ConcurrentHashMap<>(); + // 断点续传:当前文件哈希值 + private String fileHashString; + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:该数据片超时次数 + private Map timeOutNums = new ConcurrentHashMap<>(); + // 超时重传上限数值,某个兴趣包的重传次数超过此数值,则放弃重传,直接返回false + private Integer timeoutCeil=15; + + // 标记该次传输的状态 + private boolean isClosed; + + public UpLoadClientBPRenewal(File file, String serverPath, Integer facepoolnumber){ + this.file = file; + this.serverPath = serverPath; + this.facePool=new FacePool(facepoolnumber); + this.facePool.start(MinoaDataAPI.minoaServerIpString,MinoaDataAPI.minoaServerPortInteger); + System.out.println("***********FacePool中并发数:"+facepoolnumber+"***********"); +// fileHashString= EncryptionUtil.getFileHash(file.getAbsolutePath()); + fileHashString=MinoaDataAPI.getPureFileNameFromWindows(file.getAbsolutePath()) + +"_"+file.length(); + isClosed=false; + } + + // 由于点击取消传输之后弹窗关闭, + // 该文件类可能会继续发送数据,或有其它继续执行的代码 + // 这里增加一个函数进行关闭操作 + public void closeSelf(){ + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + System.out.println("取消传输时关闭文件报错:"+e.getMessage()); + } + this.facePool.shutdownNow(); + this.isClosed=true; + } + + /* + * 传入一个刚被后台成功响应的兴趣包序号, + * 判断[point,current]之间的兴趣包序号是否全部被成功响应 + */ + private boolean isAllOK(Long point,Long currentNo){ + for(Long i=point;i<=currentNo;i++){ + if(!successList.containsKey(i)){ + return false; + } + } + return true; + } + + // 移除successList中已经成功发送的部分,减少内存占用 + private void mvSuccessNum(Long fromNo,Long toNo){ + for(Long i=fromNo;i<=toNo;i++){ + successList.remove(i); + } + } + + // 对每个成功收到200应答的兴趣包执行如下程序, + // 以维持断点续传的功能 + private void refreshSuccessPoint(Long sliceNo){ + // 当前断点 + Long point=successPoint.get(); + // 加入successList + successList.put(sliceNo,true); + // 如果当前断点到新收到数据包之间的兴趣包全部成功响应, + // 更新断点,并清理内存 + if(isAllOK(point,sliceNo)){ + successPoint.getAndSet(sliceNo+1); + mvSuccessNum(point,sliceNo); + } + } + + + // 将本次传输的断点信息保存到本地 + public void setPointInfo(){ + String fileName=MinoaDataAPI.getPureFileNameFromWindows(file.getAbsolutePath()); + FileBPRUtil.setUploadBreakPointInfo( + fileHashString,fileName,serverPath,successPoint.get()); + System.out.println("保存断点信息成功!!!"); + } + + // 移除已经上传成功的文件断点信息 + private void rmPointInfo(){ + if(FileBPRUtil.isHaveUploadBreakPointInfo(fileHashString)){ + FileBPRUtil.deleteUploadBreakPointInfo(fileHashString); + } + } + + + /** + * 判断是否含有超时次数超过阈值的 + * @return + */ + private boolean isTimeOut(){ + for (Map.Entry entry : timeOutNums.entrySet()) { + System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + if(entry.getValue() > timeoutCeil){ + return true; + } + } + return false; + } + + /** + * 上传数据的主函数 + * @return + */ + public boolean uploadData(){ + // 先初始化一个本地文件 + try { + this.randomAccessFile = new RandomAccessFile(this.file.getAbsolutePath(), "rw"); + System.out.println("本地文件打开"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + // 初始化sliceNumbers + if(file.length()% MainApp.sliceSize == 0){ + sliceNumbers = file.length()/ MainApp.sliceSize; + }else{ + sliceNumbers = file.length()/ MainApp.sliceSize + 1; + } + + // 判断是否有断点续传记录 + if(FileBPRUtil.isHaveUploadBreakPointInfo(fileHashString)){ + // 有记录,则判断文件名和服务器地址是否一致 + BPRInfo bprInfo=FileBPRUtil.getUploadBreakPointInfo(fileHashString); + String fileName=MinoaDataAPI.getPureFileNameFromWindows(file.getAbsolutePath()); + if((!(bprInfo==null)) + &&bprInfo.getFileName().equals(fileName) + &&bprInfo.getFileLocator().equals(serverPath)){ + // 都一致,则更新recvCount\currentOffset\successPoint + System.out.println("即将开始断点续传..."); + recvCount.getAndSet(bprInfo.getPoint()); + currentOffset=recvCount.get()*MainApp.sliceSize; + successPoint.getAndSet(bprInfo.getPoint()); + }else{ + System.out.println("即将开始文件上传..."); + } + }else { + System.out.println("即将开始文件上传..."); + } + + while(!isClosed){ + // 判断是否已经上传完成 + if(sliceNumbers <= recvCount.longValue()){ + break; + } + + // 判断是否已经被用户终止上传 + if(MainApp.isCancelFileTransferFlag){ + System.out.println("用户已主动中断上传"); + closeSelf(); + // 保存断点信息 + setPointInfo(); + return false; + } + + //每一次发送数据 + int cwnd = 0; + //获取此次可以发送数据的个数 + while((cwnd = congestionWindow.getAvailableCwnd()) <= 0){ + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for(int i=0; i 0){ + //首先发送failedslice中的数据 + Long failOffset = failedSlice.poll(); + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(failOffset); + }else{ + if(currentOffset < file.length()){ + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(currentOffset); + currentOffset += MainApp.sliceSize; + } + } + congestionWindow.inFlightIncrease(); + congestionWindow.nOutInterestIncrease(); + } + + if(isTimeOut()){ + System.out.println("丢包超过阈值: "+timeoutCeil+" ,文件传输主动中断"); + closeSelf(); + // 保存断点信息 + setPointInfo(); + return false; + } + + } + +// try { +// this.randomAccessFile.close(); +// System.out.println("本地文件关闭"); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// this.facePool.shutdownNow(); + closeSelf(); + // 更新本地断点信息 + rmPointInfo(); + return true; + } + + /** + * 发送一个数据片 + * @param offset + */ + private void sendData(Long offset){ + facePool.submit(freeface -> { + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getUploadFile(file.getAbsolutePath(), offset); + //System.out.println("*******************:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 获取json格式的数据请求字符串 + //String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); + + // 发送数据兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong + +"/" +MainApp.loginer.getUserName()+"/"+(System.currentTimeMillis()/1000+MainApp.timeStampBias)); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + byte[] requestByte = null; // 请求数据 + // 重写ondata + try { + // 解析并修改文件名【window转server】 + String localFilePath = ""; + String serverFilePath = ""; + try { + // 解析 + JSONObject jsonObject = new JSONObject(orderInfo.getJsonString()); + localFilePath = jsonObject.getString("filename"); +// serverFilePath = serverpath + getPureFileNameFromWindows(localFilePath); + serverFilePath = serverPath + MinoaDataAPI.getPureFileNameFromWindows(localFilePath); + // 修改 + jsonObject.remove("filename"); + jsonObject.put("filename", serverFilePath); + orderInfo.setJsonString(jsonObject.toString()); + System.out.println("request head str: " + orderInfo.getJsonString()); + System.out.println("head str length: " + orderInfo.getJsonString().length()); + System.out.println("head str byte length: " + StringByteLengthUtil.getByteLength(orderInfo.getJsonString())); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 从本地读取数据 + byte[] content = new byte[10000]; // 文件分片内容 + try { + synchronized (randomAccessFile) { +// RandomAccessFile randomAccessFile = new RandomAccessFile(localFilePath, "rw"); + // 设置文件的读取起点 + try{ + randomAccessFile.seek(offset); + }catch (IOException e){ + // 说明文件流已经关闭,则不继续执行 + return ; + } + // 尝试读取数据 + int readLen = randomAccessFile.read(content, 0, 7000); + int jsonLen = StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); + requestByte = new byte[jsonLen + 1 + readLen]; + System.arraycopy(orderInfo.getJsonString().getBytes(StandardCharsets.UTF_8), 0, requestByte, 0, jsonLen); + requestByte[jsonLen] = 0; + System.arraycopy(content, 0, requestByte, jsonLen + 1, readLen); +// randomAccessFile.close(); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 赋值兴趣包的附带信息 + System.out.println("raw length: "+requestByte.length); + String requestStr=new String(requestByte,StandardCharsets.UTF_8); +// System.out.println("requestStr: "+requestStr); +// System.out.println("requestStr length: "+requestStr.getBytes(StandardCharsets.UTF_8).length); +// byte[] cache=Aes.newEncrypt(requestStr,Aes.getDefaultKey()); + byte[] cache=Aes.pureEncrypt(requestByte,Aes.getDefaultKey()); + System.out.println("aes length: "+cache.length); + interest.setApplicationParameters(new Blob(cache)); + //超时 + interest.setInterestLifetimeMilliseconds(2000); + // 密钥“黑盒” + try { +// MinoaDataAPI.keyChain.sign(interest); + KeyManager.INSTANCE.getKeyChain().sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + UpLoadData upLoadData = new UpLoadData(orderInfo.getSeq(), responseData, offset / MainApp.sliceSize); + UpLoadTimeout upLoadTimeout = new UpLoadTimeout(upLoadData); + UpLoadOnNetWorkNack upLoadOnNetWorkNack = new UpLoadOnNetWorkNack(upLoadData); + // 使用tcp发送数据请求兴趣包 + freeface.expressInterest(interest, upLoadData, upLoadTimeout, upLoadOnNetWorkNack); + + + } catch (IOException e) { + e.printStackTrace(); + } + + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + e.printStackTrace(); + } + }); + } + + + public class UpLoadData implements OnData { + + private Long seq; + private ResponseData responseData; + //第几个数据片 + public Long sliceNum; + + public boolean isFinished = false; + + public UpLoadData(Long seq, ResponseData responseData, Long sliceNum){ + this.seq = seq; + this.responseData = responseData; + this.sliceNum = sliceNum; + } + + @Override + public void onData(Interest interest, Data data){ + System.out.println("##### upload file receive one data package #####"); + if (!isClosed) { +// System.out.println("##### upload file receive one data package #####"); + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + Base64.Encoder base64Encoder = Base64.getMimeEncoder(); + String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = Aes.Decrypt(dataContentBase64,Aes.getDefaultKey()) ; + } catch (Exception e) { + e.printStackTrace(); + } + responseData.setResponseData(plainText); + JSONObject jsonObject = responseData.praseRequestData(); + + congestionWindow.inFlightDecrease(); + // 解析数据,是否成功上传 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + recvCount.incrementAndGet(); + congestionWindow.cwndIncrease(); + // 刷新断点 + refreshSuccessPoint(sliceNum); + System.out.println("第" + sliceNum + "片上传成功!"); + } else { + failedSlice.add(sliceNum * MainApp.sliceSize); + System.out.println("第" + sliceNum + "片上传失败!"); + } + //拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if (data.getCongestionMark() > 0) { + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + congestionWindow.cwndDecrease(); + } else { + //如果没有检测到拥塞,则增加窗口 + congestionWindow.nIndataIncrease(); + } + + if (timeOutNums.containsKey(sliceNum)) { + timeOutNums.remove(sliceNum); + } + + isFinished = true; + } + } + public int callbackCount = 0 ; + + } + + public class UpLoadTimeout implements OnTimeout { + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadTimeout(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onTimeout(Interest interest){ + if (!isClosed) { + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum * MainApp.sliceSize); + System.out.println("加入重传队列-slice:"+upLoadData.sliceNum); + callbackCount++; + System.out.println("Time out for interest " + interest.getName()); + upLoadData.isFinished = false; + + // + if (timeOutNums.containsKey(upLoadData.sliceNum)) { + int value = timeOutNums.get(upLoadData.sliceNum) + 1; + timeOutNums.put(upLoadData.sliceNum, value); + System.out.println("重传次数增加-"+"(slice-key: "+upLoadData.sliceNum+")、value: "+value); + } else { + timeOutNums.put(upLoadData.sliceNum, 1); + System.out.println("重传次数增加-"+"(slice-key: "+upLoadData.sliceNum+")、value: 1"); + } + } + } + } + + public class UpLoadOnNetWorkNack implements OnNetworkNack { + + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadOnNetWorkNack(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onNetworkNack(Interest var1, NetworkNack var2){ + if (!isClosed) { + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum * MainApp.sliceSize); + System.out.println("加入重传队列-slice:"+upLoadData.sliceNum); + // + if (timeOutNums.containsKey(upLoadData.sliceNum)) { + int value = timeOutNums.get(upLoadData.sliceNum) + 1; + timeOutNums.put(upLoadData.sliceNum, value); + System.out.println("重传次数增加-"+"(slice-key: "+upLoadData.sliceNum+")、value: "+value); + } else { + timeOutNums.put(upLoadData.sliceNum, 1); + System.out.println("重传次数增加-"+"(slice-key: "+upLoadData.sliceNum+")、value: 1"); + } + callbackCount++; + System.out.println("onNetworkNack " + var1.getName()); + upLoadData.isFinished = true; + } + } + + } + + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBasedFacePool.java b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBasedFacePool.java new file mode 100644 index 0000000..6a1f81f --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/UpLoadClientBasedFacePool.java @@ -0,0 +1,424 @@ +package cn.minoa.dataRequestInterface; + +import cn.minoa.MainApp; +import cn.minoa.util.Aes; +import cn.minoa.util.StringByteLengthUtil; +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author mac + * @description + * @date 2020-03-22 18:28 + */ +public class UpLoadClientBasedFacePool { + + private String orderPrefix = "/uploadFile"; + + private String serverPath; + + private File file; + //总数据片大小 + private Long sliceNumbers; + + private Long currentOffset = 0L; + + private CongestionWindow congestionWindow = new CongestionWindow(); + + private FacePool facePool; + + // 要上传的本地文件对象 + private RandomAccessFile randomAccessFile; + + //发送失败,需要重新发送的数据。key:第几个数据片,从0开始计数,value:文件偏移 +// private Map failedSlice = new ConcurrentHashMap<>(); + //存放发送失败数据的文件偏移 + private ConcurrentLinkedQueue failedSlice = new ConcurrentLinkedQueue<>(); + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:是否发送成功 + private Map sendRes = new ConcurrentHashMap<>(); + + //收到回复的数量 + public AtomicLong recvCount = new AtomicLong(0); + + //发送的数据是否发送成功,key:第几个数据片,从0开始,value:该数据片超时次数 + private Map timeOutNums = new ConcurrentHashMap<>(); + // 超时重传上限数值,某个兴趣包的重传次数超过此数值,则放弃重传,直接返回false + private Integer timeoutCeil=15; + + public UpLoadClientBasedFacePool(File file, String serverPath,Integer facepoolnumber){ + this.file = file; + this.serverPath = serverPath; + this.facePool=new FacePool(facepoolnumber); + this.facePool.start(MinoaDataAPI.minoaServerIpString,MinoaDataAPI.minoaServerPortInteger); + System.out.println("***********FacePool中并发数:"+facepoolnumber+"***********"); + } + + /** + * 判断是否含有超时次数超过阈值的 + * @return + */ + private boolean isTimeOut(){ + for (Map.Entry entry : timeOutNums.entrySet()) { + System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + if(entry.getValue() > timeoutCeil){ + return true; + } + } + return false; + } + + /** + * 上传数据的主函数 + * @return + */ + public boolean uploadData(){ + // 先初始化一个本地文件 + try { + this.randomAccessFile = new RandomAccessFile(this.file.getAbsolutePath(), "rw"); + System.out.println("本地文件打开"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + if(file.length()% MainApp.sliceSize == 0){ + sliceNumbers = file.length()/ MainApp.sliceSize; + }else{ + sliceNumbers = file.length()/ MainApp.sliceSize + 1; + } + + while(true){ + //判断是否已经上传安成 + if(sliceNumbers <= recvCount.longValue()){ + break; + } + + // 判断是否已经被用户终止上传 + if(MainApp.isCancelFileTransferFlag){ + System.out.println("用户已主动中断上传"); + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return false; + } + + //每一次发送数据 + int cwnd = 0; + //获取此次可以发送数据的个数 + while((cwnd = congestionWindow.getAvailableCwnd()) <= 0){ + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for(int i=0; i 0){ + //首先发送failedslice中的数据 + Long failOffset = failedSlice.poll(); + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(failOffset); + }else{ + if(currentOffset < file.length()){ + System.out.println("发送数据, name: " + file.getName() +",offset:" + currentOffset + ", failedSlice:" + failedSlice.size() ); + sendData(currentOffset); + currentOffset += MainApp.sliceSize; + } + } + congestionWindow.inFlightIncrease(); + congestionWindow.nOutInterestIncrease(); + + } + + if(isTimeOut()){ + System.out.println("丢包超过阈值: "+timeoutCeil+" ,文件传输主动中断"); + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return false; + } + + } + + try { + this.randomAccessFile.close(); + System.out.println("本地文件关闭"); + } catch (IOException e) { + e.printStackTrace(); + } + this.facePool.shutdownNow(); + return true; + } + + /** + * 发送一个数据片 + * @param offset + */ + private void sendData(Long offset){ + facePool.submit(freeface -> { + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getUploadFile(file.getAbsolutePath(), offset); + //System.out.println("*******************:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 获取json格式的数据请求字符串 + //String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); + + // 发送数据兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + orderPrefix + "/" + seqLong + +"/" +MainApp.loginer.getUserName()+"/"+(System.currentTimeMillis()/1000)); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + try { + ResponseData responseData = new ResponseData(); + responseData.setPrefix(orderPrefix); + byte[] requestByte = null; // 请求数据 + // 重写ondata + try { + // 解析并修改文件名【window转server】 + String localFilePath = ""; + String serverFilePath = ""; + try { + // 解析 + JSONObject jsonObject = new JSONObject(orderInfo.getJsonString()); + localFilePath = jsonObject.getString("filename"); +// serverFilePath = serverpath + getPureFileNameFromWindows(localFilePath); + serverFilePath = serverPath + MinoaDataAPI.getPureFileNameFromWindows(localFilePath); + // 修改 + jsonObject.remove("filename"); + jsonObject.put("filename", serverFilePath); + orderInfo.setJsonString(jsonObject.toString()); + System.out.println("request head str: " + orderInfo.getJsonString()); + System.out.println("head str length: " + orderInfo.getJsonString().length()); + System.out.println("head str byte length: " + StringByteLengthUtil.getByteLength(orderInfo.getJsonString())); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 从本地读取数据 + byte[] content = new byte[10000]; // 文件分片内容 + try { + synchronized (randomAccessFile) { +// RandomAccessFile randomAccessFile = new RandomAccessFile(localFilePath, "rw"); + // 设置文件的读取起点 + randomAccessFile.seek(offset); + // 尝试读取数据 + int readLen = randomAccessFile.read(content, 0, 7000); + int jsonLen = StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); + requestByte = new byte[jsonLen + 1 + readLen]; + System.arraycopy(orderInfo.getJsonString().getBytes(StandardCharsets.UTF_8), 0, requestByte, 0, jsonLen); + requestByte[jsonLen] = 0; + System.arraycopy(content, 0, requestByte, jsonLen + 1, readLen); +// randomAccessFile.close(); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // AES加密 + String requestStr=null; + try { + requestStr= Aes.Encrypt(new String(requestByte)); + } catch (Exception e) { + System.out.println("AES加密错误:"+e.getMessage()); + } + // 赋值兴趣包的附带信息,并标志区块链 + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(requestStr))); + //超时 + interest.setInterestLifetimeMilliseconds(5000); + // 密钥“黑盒” + try { +// MinoaDataAPI.keyChain.sign(interest); + KeyManager.INSTANCE.getKeyChain().sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + UpLoadData upLoadData = new UpLoadData(orderInfo.getSeq(), responseData, offset / MainApp.sliceSize); + UpLoadTimeout upLoadTimeout = new UpLoadTimeout(upLoadData); + UpLoadOnNetWorkNack upLoadOnNetWorkNack = new UpLoadOnNetWorkNack(upLoadData); + // 使用tcp发送数据请求兴趣包 +// MinoaDataAPI.runFace(); + freeface.expressInterest(interest, upLoadData, upLoadTimeout, upLoadOnNetWorkNack); + + + } catch (IOException e) { + e.printStackTrace(); + } + + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + e.printStackTrace(); + } + }); + } + + + public class UpLoadData implements OnData { + + private Long seq; + private ResponseData responseData; + //第几个数据片 + public Long sliceNum; + + public boolean isFinished = false; + + public UpLoadData(Long seq, ResponseData responseData, Long sliceNum){ + this.seq = seq; + this.responseData = responseData; + this.sliceNum = sliceNum; + } + + @Override + public void onData(Interest interest, Data data){ + if (!MainApp.isCancelFileTransferFlag) { + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + Base64.Encoder base64Encoder = Base64.getMimeEncoder(); + String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = Aes.Decrypt(dataContentBase64,"1234567890abcdef") ; + } catch (Exception e) { + e.printStackTrace(); + } + responseData.setResponseData(plainText); + JSONObject jsonObject = responseData.praseRequestData(); + + congestionWindow.inFlightDecrease(); + // 解析数据,是否成功上传 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + recvCount.incrementAndGet(); + congestionWindow.cwndIncrease(); + System.out.println("第" + sliceNum + "片上传成功!"); + } else { + failedSlice.add(sliceNum * MainApp.sliceSize); + System.out.println("第" + sliceNum + "片上传失败!"); + } + //拥塞标记返回值,无意义,大于0就表示拥塞,直接进行窗口减小操作即可 + if (data.getCongestionMark() > 0) { + System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&网络拥塞,滑动窗口减小,getCongestionMark:" + data.getCongestionMark()); + congestionWindow.cwndDecrease(); + } else { + //如果没有检测到拥塞,则增加窗口 + congestionWindow.nIndataIncrease(); + } + + if (timeOutNums.containsKey(sliceNum)) { + timeOutNums.remove(sliceNum); + } + + isFinished = true; + } + } + public int callbackCount = 0 ; + + } + + public class UpLoadTimeout implements OnTimeout { + + + + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadTimeout(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onTimeout(Interest interest){ + if (!MainApp.isCancelFileTransferFlag) { + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum * MainApp.sliceSize); + callbackCount++; + System.out.println("Time out for interest " + interest.getName()); + upLoadData.isFinished = false; + + // + if (timeOutNums.containsKey(upLoadData.sliceNum)) { + int value = timeOutNums.get(upLoadData.sliceNum) + 1; + timeOutNums.put(upLoadData.sliceNum, value); + } else { + timeOutNums.put(upLoadData.sliceNum, 1); + } + } + } + } + + public class UpLoadOnNetWorkNack implements OnNetworkNack { + + private UpLoadData upLoadData; + public int callbackCount = 0 ; + + public UpLoadOnNetWorkNack(UpLoadData upLoadData) { + this.upLoadData = upLoadData; + } + + @Override + public void onNetworkNack(Interest var1, NetworkNack var2){ + if (!MainApp.isCancelFileTransferFlag) { + congestionWindow.inFlightDecrease(); + failedSlice.add(upLoadData.sliceNum * MainApp.sliceSize); + // + if (timeOutNums.containsKey(upLoadData.sliceNum)) { + int value = timeOutNums.get(upLoadData.sliceNum) + 1; + timeOutNums.put(upLoadData.sliceNum, value); + } else { + timeOutNums.put(upLoadData.sliceNum, 1); + } + callbackCount++; + System.out.println("onNetworkNack " + var1.getName()); + upLoadData.isFinished = true; + } + } + + } + + +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/UploadFileManager.java b/src/main/java/cn/minoa/dataRequestInterface/UploadFileManager.java new file mode 100644 index 0000000..d6f3079 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/UploadFileManager.java @@ -0,0 +1,263 @@ +//package cn.minoa.dataRequestInterface; +//// 多线程上传下载:上传控制器【1/3】 +//import java.util.ArrayList; +//import java.util.List; +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.atomic.AtomicInteger; +// +///** +// * @author mac +// * @description +// * @date 2020-03-13 11:10 +// */ +//public class UploadFileManager { +// +// //上传文件成功的次数 +// private Integer successCount = 0; +// public AtomicInteger successUploadedDataSlices=new AtomicInteger(0); +// +// //文件在本地的绝对路径 +// private String fileAbsolutePath; +// +// //文件在服务器上的路径 +// private String serverPath; +// +// //上传文件的线程数 +// private Integer threadNumber; +// +// //文件分片大小 +// private Integer sliceSize; +// +// //文件大小 +// private Long fileSize; +// +// //文件的分片数量 +// private Integer sliceNumbers; +// +// //多线程上传的话,每个线程需要上传的分片数量 +// private Integer sliceEveryThread; +// +// private List list = new ArrayList<>(); +// +//// private MainApp mainApp; +// +// private CountDownLatch latch; +// +// public UploadFileManager(String fileAbsolutePath, String serverPath, +// Integer threadNumber, Integer sliceSize, Long fileSize) { +// this.fileAbsolutePath = fileAbsolutePath; +// this.serverPath = serverPath; +// this.threadNumber = threadNumber; +// this.sliceSize = sliceSize; +// this.fileSize = fileSize; +// } +// +// +//// public UploadFileManager(MainApp mainApp,String fileAbsolutePath, String serverPath, +//// Integer threadNumber, Integer sliceSize, Long fileSize) { +//// this.mainApp=mainApp; +//// this.fileAbsolutePath = fileAbsolutePath; +//// this.serverPath = serverPath; +//// this.threadNumber = threadNumber; +//// this.sliceSize = sliceSize; +//// this.fileSize = fileSize; +//// } +// +// @Override +// public String toString() { +// return "UploadFileManager{" + +// "successCount=" + successCount + +// ", fileAbsolutePath='" + fileAbsolutePath + '\'' + +// ", serverPath='" + serverPath + '\'' + +// ", threadNumber=" + threadNumber + +// ", sliceSize=" + sliceSize + +// ", fileSize=" + fileSize + +// ", sliceNumbers=" + sliceNumbers + +// ", sliceEveryThread=" + sliceEveryThread + +// '}'; +// } +// +// public int start() { +// +// long startTime = System.currentTimeMillis(); +// //计算分片,查看每个子线程需要上传的分片情况 +// +// //需要的分片数量 = sliceNumbers +// sliceNumbers = (int) (fileSize / sliceSize) + 1; +// +// +// //每个线程需要上传的分片数量 +//// sliceEveryThread = sliceNumbers/threadNumber + 1; +// sliceEveryThread = sliceNumbers / threadNumber; +// +// System.out.println("****=========:" + toString()); +// +// if (sliceNumbers <= threadNumber) { +// System.out.println("多线程上传,分片数量小于线程数量"); +// //分片的数量小于线程数量,则每个线程上传一个分片 +// //根据分片数量初始化latch +// latch = new CountDownLatch(sliceNumbers); +// for (int i = 0; i < sliceNumbers; i++) { +// +// UploadThread uploadThread = new UploadThread(i * sliceSize); +// list.add(uploadThread); +// new Thread(uploadThread).start(); +// } +// } else { +// System.out.println("多线程上传,分片数量大于线程数量"); +// //根据分片数量初始化latch +// latch = new CountDownLatch(threadNumber + 1); +// +// Integer sliceOffset = 0, i; +// //若分片数量大于线程数量 +// for (i = 0; i < threadNumber; i++) { +// +// sliceOffset = sliceSize * i * sliceEveryThread; +//// System.out.println("第" + i + "个线程的起始偏移是:" + sliceOffset); +// UploadThread uploadThread; +// /* if(i == (threadNumber - 1)){ +// uploadThread = new UploadThread(sliceOffset, sliceNumbers ); +//// System.out.println("第" + i + "次上传,分片区间是:[" + (i * sliceEveryThread) + ", " + (i * sliceEveryThread + sliceNumbers) + ")" ); +// }else{*/ +// uploadThread = new UploadThread(sliceOffset, sliceEveryThread); +//// System.out.println("第" + i + "次上传,分片区间是:[" + (i * sliceEveryThread) + ", " + (i * sliceEveryThread + sliceEveryThread) + ")" ); +//// } +// sliceNumbers -= sliceEveryThread; +// list.add(uploadThread); +// new Thread(uploadThread).start(); +// } +// sliceOffset = sliceSize * i * sliceEveryThread; +// System.out.println("---------offset:" + sliceOffset + ", slicenum:" + (sliceNumbers % threadNumber)); +// UploadThread uploadThread = new UploadThread(sliceOffset, sliceNumbers % threadNumber); +// list.add(uploadThread); +// new Thread(uploadThread).start(); +// +// } +// +// +// //等待各个子线程上传完成 +// try { +// latch.await(); +// +// for (UploadThread up : list) { +// successCount += up.getSuccessCount(); +// } +// +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// +//// System.out.println(toString()); +// long endTime = System.currentTimeMillis(); +// +// System.out.println("总耗时:" + (endTime - startTime) + "ms"); +// +// return successCount; +// } +// +// +// class UploadThread implements Runnable { +// +// //上传文件成功的次数 +// private Integer successCount = 0; +// +// //线程上传文件的起始偏移 +// private Integer offset; +// +// //需要上传的分片数量 +// private Integer sliceNumbers; +// +// +// public UploadThread(Integer offset, Integer sliceNumbers) { +// this.offset = offset; +// this.sliceNumbers = sliceNumbers; +// this.successCount = 0; +// } +// +// public UploadThread(Integer offset) { +// +// this.successCount = 0; +// this.offset = offset; +// this.sliceNumbers = 1; +// } +// +// @Override +// public void run() { +// +// for (int i = 0; i < sliceNumbers; i++) { +// System.out.println("第" + i + "个线程的起始偏移是:" + offset + (i * sliceSize)); +// boolean isSuccessful = DownAndUpLoadData.uploadOrder("/uploadFile", fileAbsolutePath, offset + (i * sliceSize), serverPath); +// if (isSuccessful) { +// successCount++; +// } +// // wefree +// successUploadedDataSlices.getAndIncrement(); +// } +// +// System.out.println("上传成功次数是:" + successCount + "@ UploadFileManager 176行"); +// +// latch.countDown(); +// } +// +// public Integer getOffset() { +// return offset; +// } +// +// public void setOffset(Integer offset) { +// this.offset = offset; +// } +// +// public Integer getSliceNumbers() { +// return sliceNumbers; +// } +// +// public void setSliceNumbers(Integer sliceNumbers) { +// this.sliceNumbers = sliceNumbers; +// } +// +// public Integer getSuccessCount() { +// return successCount; +// } +// } +// +// +// public String getFileAbsolutePath() { +// return fileAbsolutePath; +// } +// +// public void setFileAbsolutePath(String fileAbsolutePath) { +// this.fileAbsolutePath = fileAbsolutePath; +// } +// +// public String getServerPath() { +// return serverPath; +// } +// +// public void setServerPath(String serverPath) { +// this.serverPath = serverPath; +// } +// +// public Integer getThreadNumber() { +// return threadNumber; +// } +// +// public void setThreadNumber(Integer threadNumber) { +// this.threadNumber = threadNumber; +// } +// +// public Integer getSliceSize() { +// return sliceSize; +// } +// +// public void setSliceSize(Integer sliceSize) { +// this.sliceSize = sliceSize; +// } +// +// public Long getFileSize() { +// return fileSize; +// } +// +// public void setFileSize(Long fileSize) { +// this.fileSize = fileSize; +// } +//} diff --git a/src/main/java/cn/minoa/dataRequestInterface/creatNDNKey.java b/src/main/java/cn/minoa/dataRequestInterface/creatNDNKey.java new file mode 100644 index 0000000..496c2aa --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/creatNDNKey.java @@ -0,0 +1,36 @@ +package cn.minoa.dataRequestInterface; + +import net.named_data.jndn.*; +import net.named_data.jndn.security.KeyChain; + +import net.named_data.jndn.security.pib.PibIdentity; +import net.named_data.jndn.security.pib.PibSqlite3; +import net.named_data.jndn.security.tpm.TpmBackEndMemory; +//import net.named_data.jndn.util.Blob; +//import org.json.JSONObject; + +//生成密钥 +public class creatNDNKey { + static private String pidDbPathString=""; +// static private String pidDbPathString="E:\\MyCode\\newEclipseWorkspace\\minoa_client_windows_JavaFX11"; + static private String keyChainNameString="/wefree"; + static private String pidIdentityString="wefree"; + + public static void main(String args[]){ + KeyChain keyChain ; + try{ + //密钥“黑盒” + PibSqlite3 pibSqlite3 = new PibSqlite3(pidDbPathString); + TpmBackEndMemory tpmBackEndMemory = new TpmBackEndMemory() ; + keyChain = new KeyChain(pibSqlite3,tpmBackEndMemory ) ; + keyChain.createIdentityV2(new Name(keyChainNameString)) ; + PibIdentity identity = keyChain.getPib().getIdentity(new Name(pidIdentityString)); ; + keyChain.setDefaultIdentity(identity); + //输出测试 + System.out.println(keyChain.getDefaultCertificateName().toUri()); + System.out.println(keyChain.getPib().getPibLocator()); + }catch (Exception e){ + System.out.println("exception: " + e.getMessage()); + } + } +} diff --git a/src/main/java/cn/minoa/dataRequestInterface/registerUser.java b/src/main/java/cn/minoa/dataRequestInterface/registerUser.java new file mode 100644 index 0000000..a995839 --- /dev/null +++ b/src/main/java/cn/minoa/dataRequestInterface/registerUser.java @@ -0,0 +1,29 @@ +package cn.minoa.dataRequestInterface; +// 在注册的界面未完成之前用于注册新用户 + +import org.json.JSONObject; + +public class registerUser { + // 获取注册请求字符串 + @SuppressWarnings("unused") + static private String getRegisterJson() { + JSONObject jsonObject = new JSONObject(); + String mPubKey = "Bv0CsQcuCAR3Z2gwCANLRVkICH9+4yjVUHZaCAxjZXJ0LXJlcXVlc3QICf0AAAFv\nEUSBehQJGAECGQQANu6AFf0BJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBANbFutCAZqFjHEa16tYvy4lBgi9/zixKWQzE9I72vaG2Imym+6KR30qkAQOK\nh0uE5RD2YsGhRK99rde+JOqIfk2/sunhp4nctOS5f4DeuOty58ekrO+Co9ZFmNBI\nmyk5i/hakjBEd/vdD7b1VadDRep5mWs3rgCinZOUfVaZ4oKqSws7ZWMvOSlRMOOi\nVzj53P4poY109S6aWmi3xKw/D76vfJapjdOB9Dg7S3rz0Rve1F/ajHCXWLt4XU3k\nUCiBn3Qmt6UD/MyDBaaUCfOvHTN6yVPtN6aOtYJVBAWt/P1ahLYhWkSm6KvrwMt0\nRzy2lw/lPriT99REItc/0wK6XwkCAwEAARZGGwEBHBcHFQgEd2doMAgDS0VZCAh/\nfuMo1VB2Wv0A/Sb9AP4PMjAxOTEyMTdUMDAzMTQw/QD/DzIwMTkxMjI3VDAwMzEz\nORf9AQCM+8WROXfi8WD9DydfLuSUxV0v3KW/Gn422/puDrkwwi1R2ogsAUc2O8pp\npuMLjWrQOTgE6ZTg80uWoDSUjjk44aMjkL2SAfp62vHRbGFx7+dckHAtF+DS6NH7\nX2vojFHOg6fksOE1M7XqLHmEEctZrFVSnQO7nB3DfRg+BqzZC/iy+8YAvTIvqA1c\nNsKYkmNITB80aM1v2H7fKnV4ZJpZnW71G5vbaPUseeklalQ2vEuBgWdd/1PL/0Ml\nI52npzqoeKAEpLp+vBwGN5ryGtWKB12nNimpMfevQAn/9BVIg59lZ7DIlE0sWmgW\nnwDxIXKebdViOejHlKwNAykxRRWw"; + String commandString = "/register"; + String userNameString = "oa_wefree"; + String passWordString = "12345678"; + String phoneString = "10000000006"; + Integer levelInteger = 0; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + jsonObject.put("phone", phoneString); + jsonObject.put("publicKey", mPubKey); + jsonObject.put("level", levelInteger); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} diff --git a/src/main/java/cn/minoa/model/BPRInfo.java b/src/main/java/cn/minoa/model/BPRInfo.java new file mode 100644 index 0000000..58833ce --- /dev/null +++ b/src/main/java/cn/minoa/model/BPRInfo.java @@ -0,0 +1,50 @@ +package cn.minoa.model; +// 断点续传中,需要持久化到本地的文件参数 + +public class BPRInfo { + + // 文件唯一标识:上传中指的是哈希值,下载中指的是文件服务器URL + // 现在上传和下载的该数值全部定义为:纯文件名_文件长度 + private String fileId; + // 文件名称 + private String fileName; + // 文件位置:上传中指的是文件服务器位置,下载中指的是文件本地位置 + private String fileLocator; + // 断点续传位置 + private Long point; + + public BPRInfo(){ + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFileLocator() { + return fileLocator; + } + + public void setFileLocator(String fileLocator) { + this.fileLocator = fileLocator; + } + + public Long getPoint() { + return point; + } + + public void setPoint(Long point) { + this.point = point; + } +} diff --git a/src/main/java/cn/minoa/model/Email.java b/src/main/java/cn/minoa/model/Email.java new file mode 100644 index 0000000..c51dabb --- /dev/null +++ b/src/main/java/cn/minoa/model/Email.java @@ -0,0 +1,103 @@ +package cn.minoa.model; +//邮件类 + +import java.util.LinkedList; +import java.util.List; + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class Email { + // 邮件发送方proposer + private StringProperty sender; + // 邮件接收方approver + private StringProperty receiver; + // 邮件名称detail_title + private StringProperty title; + // 邮件内容detail_content + private StringProperty content; + // 邮件编号 + private IntegerProperty id; + // 邮件中的附件列表 + public List files; + + public Email() { + // TODO Auto-generated constructor stub + title = new SimpleStringProperty(); + content = new SimpleStringProperty(); + sender = new SimpleStringProperty(); + receiver = new SimpleStringProperty(); + id = new SimpleIntegerProperty(); + files=new LinkedList(); + } + + // id + public void setId(Integer id) { + this.id.set(id); + } + public Integer getId() { + return this.id.get(); + } + public IntegerProperty idProperty() { + return id; + } + + // sender + public void setSender(String sender) { + this.sender.set(sender); + } + + public String getSender() { + return this.sender.get(); + } + + public StringProperty senderProperty() { + return sender; + } + + // receiver + public void setReceiver(String receiver) { + this.receiver.set(receiver); + } + + public String getReceiver() { + return this.receiver.get(); + } + + public StringProperty receiverProperty() { + return receiver; + } + + // title + public void setTitle(String title) { + this.title.set(title); + } + + public String getTitle() { + return title.get(); + } + + public StringProperty titleProperty() { + return title; + } + + // content + public void setContent(String content) { + this.content.set(content); + } + + public String getContent() { + return content.get(); + } + + public StringProperty contentProperty() { + return content; + } + + // 获取附件文件数目 + public Integer getFilesNumber() { + return files.size(); + } +} diff --git a/src/main/java/cn/minoa/model/Loginer.java b/src/main/java/cn/minoa/model/Loginer.java new file mode 100644 index 0000000..68f3911 --- /dev/null +++ b/src/main/java/cn/minoa/model/Loginer.java @@ -0,0 +1,114 @@ +package cn.minoa.model; +//存储登录者信息 + +import javafx.beans.property.SimpleStringProperty; +//import javafx.beans.property.IntegerProperty; +//import javafx.beans.property.LongProperty; +//import javafx.beans.property.ObjectProperty; +//import javafx.beans.property.SimpleIntegerProperty; +//import javafx.beans.property.SimpleObjectProperty; +//import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class Loginer { + //用户名 + private StringProperty userName; + //密码 + private StringProperty passWord; + //连接码,半小时更新一次 + private StringProperty uuid; + //其它非关键数据待补充 + private StringProperty realName; + private StringProperty phone; + private StringProperty prefix; + + public Loginer() { + this.userName=new SimpleStringProperty(); + this.passWord=new SimpleStringProperty(); + this.uuid=new SimpleStringProperty(); + this.realName=new SimpleStringProperty(); + this.phone=new SimpleStringProperty(); + this.prefix=new SimpleStringProperty(); + } + + public Loginer(String userName,String password) { + this.userName=new SimpleStringProperty(userName); + this.passWord=new SimpleStringProperty(password); + this.uuid=new SimpleStringProperty(); + this.realName=new SimpleStringProperty(); + this.phone=new SimpleStringProperty(); + this.prefix=new SimpleStringProperty(); + } + + //打印输出当前信息 + public void printSelf() { + System.out.println("**********loginer information***********"); + System.out.println("userName: "+userName.get()); + System.out.println("passWord: "+passWord.get()); + System.out.println("uuid: "+uuid.get()); + System.out.println("phone: "+phone.get()); + System.out.println("realName: "+realName.get()); + System.out.println("prefix: "+prefix.get()); + System.out.println("****************************************"); + } + + //username + public String getUserName() { + return userName.get(); + } + public void setUserName(String userName) { + this.userName.set(userName); + } + public StringProperty userNameProperty() { + return userName; + } + + //password + public String getPassWord() { + return passWord.get(); + } + public void setPassWord(String passWord) { + this.passWord.set(passWord); + } + public StringProperty passWordProperty() { + return passWord; + } + + //uuid + public String getUuid() { + return uuid.get(); + } + public void setUuid(String uuid) { + this.uuid.setValue(uuid); + } + public StringProperty uuidProperty() { + return uuid; + } + + //realName + public String getRealName(){return realName.get();} + public void setRealName(String realName){this.realName.setValue(realName);} + public StringProperty realnameProperty(){return realName;} + + //phone + public String getPhone() { + return phone.get(); + } + public void setPhone(String phone) { + this.phone.setValue(phone); + } + public StringProperty phoneProperty() { + return phone; + } + + // + public String getPrefix() { + return prefix.get(); + } + public void setPrefix(String prefix) { + this.prefix.setValue(prefix); + } + public StringProperty prefixProperty() { + return prefix; + } +} diff --git a/src/main/java/cn/minoa/model/NotReadMessageList.java b/src/main/java/cn/minoa/model/NotReadMessageList.java new file mode 100644 index 0000000..aaab93a --- /dev/null +++ b/src/main/java/cn/minoa/model/NotReadMessageList.java @@ -0,0 +1,79 @@ +package cn.minoa.model; +//维持一个没有被读取的新消息列表【作为消息的缓冲】 + +import java.util.LinkedList; +import java.util.List; + +public class NotReadMessageList { + //存储未读消息 + private List notReadMessageList; + + //构造函数 + public NotReadMessageList() { + // TODO Auto-generated constructor stub + notReadMessageList=new LinkedList(); + } + + //填充单个未读消息 + public void addNotReadMessage(SingleMessage singleMessage) { + notReadMessageList.add(singleMessage); + } + + //获取来自某个联系人的未读消息个数 + public Integer getNotReadMessageNumberByPerson(Person person) { + Integer notReadMessageNumberByPersonInteger=0; + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(person.getUserName())) { + notReadMessageNumberByPersonInteger++; + } + } + return notReadMessageNumberByPersonInteger; + } + public Integer getNotReadMessageNumberByPerson(String personname) { + Integer notReadMessageNumberByPersonInteger=0; + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(personname)) { + notReadMessageNumberByPersonInteger++; + } + } + return notReadMessageNumberByPersonInteger; + } + + //获取来自某个联系人的所有未读消息 + public List getNotReadMessageByPerson(Person person){ + List list=new LinkedList(); + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(person.getUserName())) { + list.add(notReadMessageList.get(i)); + } + } + return list; + } + public List getNotReadMessageByPerson(String personname){ + List list=new LinkedList(); + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(personname)) { + list.add(notReadMessageList.get(i)); + } + } + return list; + } + + //去除来自某个联系人的所有未读消息 + public void removeNotReadMessageByPerson(Person person) { + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(person.getUserName())) { + notReadMessageList.remove(i); + i--; + } + } + } + public void removeNotReadMessageByPerson(String personname) { + for (int i = 0; i < notReadMessageList.size(); i++) { + if(notReadMessageList.get(i).getFromName().equals(personname)) { + notReadMessageList.remove(i); + i--; + } + } + } +} diff --git a/src/main/java/cn/minoa/model/Person.java b/src/main/java/cn/minoa/model/Person.java new file mode 100644 index 0000000..8ad38e7 --- /dev/null +++ b/src/main/java/cn/minoa/model/Person.java @@ -0,0 +1,29 @@ +package cn.minoa.model; +//存储系统用户/联系人信息 + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class Person { + //用户名 + private StringProperty userName; + + //构造函数 + public Person() { + this.userName=new SimpleStringProperty(); + } + public Person(String userName) { + this.userName=new SimpleStringProperty(userName); + } + + //username + public String getUserName() { + return userName.get(); + } + public void setUserName(String userName) { + this.userName.set(userName); + } + public StringProperty userNameProperty() { + return userName; + } +} diff --git a/src/main/java/cn/minoa/model/Reimbursement.java b/src/main/java/cn/minoa/model/Reimbursement.java new file mode 100644 index 0000000..96f6cbc --- /dev/null +++ b/src/main/java/cn/minoa/model/Reimbursement.java @@ -0,0 +1,164 @@ +package cn.minoa.model; +//报销类 + +import java.util.LinkedList; +import java.util.List; + +import javafx.beans.property.*; + +public class Reimbursement { + // 报销发起方 + private StringProperty sender; + // 报销审批方 + private StringProperty receiver; + // 报销编号 + private IntegerProperty id; + // 报销状态 + private IntegerProperty process; + //报销日期 + private LongProperty date; + // 报销内容 + private StringProperty content; + // 报销单据张数 + private IntegerProperty number; + //报销总价值 + private DoubleProperty totalMoney; + //报销费用详细 + private StringProperty detail; + // 报销中的附件列表 + public List files; + + public Reimbursement() { + // TODO Auto-generated constructor stub + detail = new SimpleStringProperty(); + totalMoney=new SimpleDoubleProperty(); + number = new SimpleIntegerProperty(); + date =new SimpleLongProperty(); + process=new SimpleIntegerProperty(); + content = new SimpleStringProperty(); + sender = new SimpleStringProperty(); + receiver = new SimpleStringProperty(); + id = new SimpleIntegerProperty(); + files=new LinkedList(); + } + + // id + public void setId(Integer id) { + this.id.set(id); + } + public Integer getId() { + return this.id.get(); + } + public IntegerProperty idProperty() { + return id; + } + + // sender + public void setSender(String sender) { + this.sender.set(sender); + } + + public String getSender() { + return this.sender.get(); + } + + public StringProperty senderProperty() { + return sender; + } + + // receiver + public void setReceiver(String receiver) { + this.receiver.set(receiver); + } + + public String getReceiver() { + return this.receiver.get(); + } + + public StringProperty receiverProperty() { + return receiver; + } + + //process + public void setProcess(Integer process){ + this.process.set(process); + } + + public Integer getProcess(){ + return process.get(); + } + + public IntegerProperty processProperty(){ + return process; + } + + //date + public void setDate(Long date){ + this.date.set(date); + } + + public Long getDate(){ + return date.get(); + } + + public LongProperty dateProperty(){ + return date; + } + + //number + public void setNumber(Integer number){ + this.number.set(number); + } + + public Integer getNumber(){ + return number.get(); + } + + public IntegerProperty numberProperty(){ + return number; + } + + //totalMoney + public void setTotalMoney(Double totalMoney){ + this.totalMoney.set(totalMoney); + } + + public Double getTotalMoney(){ + return totalMoney.get(); + } + + public DoubleProperty totalMoneyProperty(){ + return totalMoney; + } + + // detail + public void setDetail(String detail) { + this.detail.set(detail); + } + + public String getDetail() { + return detail.get(); + } + + public StringProperty detailProperty() { + return detail; + } + + // content + public void setContent(String content) { + this.content.set(content); + } + + public String getContent() { + return content.get(); + } + + public StringProperty contentProperty() { + return content; + } + + // 获取附件文件数目 + public Integer getFilesNumber() { + return files.size(); + } +} diff --git a/src/main/java/cn/minoa/model/SingleFile.java b/src/main/java/cn/minoa/model/SingleFile.java new file mode 100644 index 0000000..a70e688 --- /dev/null +++ b/src/main/java/cn/minoa/model/SingleFile.java @@ -0,0 +1,73 @@ +package cn.minoa.model; +// 存储一个file或一个文件夹的属性 + +public class SingleFile { + //文件名 + private String filename; + //文件路径 + private String filepath; + //文件ID + private String fileId; + //文件创建时间 + private Long time; + //文件类型 + private String type; + //文件大小 + private Long size; + + public SingleFile(){ + } + + public SingleFile(String fileId){ + this.fileId=fileId; + } +// public SingleFile(){} + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getFilepath() { + return filepath; + } + + public void setFilepath(String filepath) { + this.filepath = filepath; + } + + public Long getTime() { + return time; + } + + public void setTime(Long time) { + this.time = time; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } +} diff --git a/src/main/java/cn/minoa/model/SingleMessage.java b/src/main/java/cn/minoa/model/SingleMessage.java new file mode 100644 index 0000000..5a9b2e0 --- /dev/null +++ b/src/main/java/cn/minoa/model/SingleMessage.java @@ -0,0 +1,99 @@ +package cn.minoa.model; +//单条消息记录 + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class SingleMessage { + private IntegerProperty msgId; + private StringProperty fromName; + private StringProperty toName; + private LongProperty time; + private StringProperty msg; + //与前端对接使用的 + private StringProperty timeString; + + //构造函数 + public SingleMessage() { + // TODO Auto-generated constructor stub + this.msgId=new SimpleIntegerProperty(); + this.fromName=new SimpleStringProperty(); + this.toName=new SimpleStringProperty(); + this.time=new SimpleLongProperty(); + this.timeString=new SimpleStringProperty(); + this.msg=new SimpleStringProperty(); + } + + //msgId + public Integer getMsgId() { + return msgId.get(); + } + public void setMsgId(Integer msgId) { + this.msgId.set(msgId); + } + public IntegerProperty msgIdProperty() { + return msgId; + } + + //fromName + public String getFromName() { + return fromName.get(); + } + public void setFromName(String fromName) { + this.fromName.set(fromName); + } + public StringProperty fromNameProperty() { + return fromName; + } + + //toName + public String getToName() { + return toName.get(); + } + public void setToName(String toName) { + this.toName.set(toName); + } + public StringProperty toNameProperty() { + return toName; + } + + //time + public Long getTime() { + return time.get(); + } + public void setTime(Long time) { + this.time.set(time); + this.timeString.set(String.valueOf(time)); + } + public LongProperty timeProperty() { + return time; + } + + //timeString + public String getTimeString() { + return timeString.get(); + } + public void setTimeString(String timeString) { + this.timeString.set(timeString); + this.time.set(Long.valueOf(timeString)); + } + public StringProperty timeStringProperty() { + return timeString; + } + + //msg + public String getMsg() { + return msg.get(); + } + public void setMsg(String msg) { + this.msg.set(msg); + } + public StringProperty msgProperty() { + return msg; + } + +} diff --git a/src/main/java/cn/minoa/model/SingleNotice.java b/src/main/java/cn/minoa/model/SingleNotice.java new file mode 100644 index 0000000..1d70759 --- /dev/null +++ b/src/main/java/cn/minoa/model/SingleNotice.java @@ -0,0 +1,116 @@ +package cn.minoa.model; +//单条通知 + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class SingleNotice { + private IntegerProperty noticeId; + private StringProperty title; + private StringProperty content; + private StringProperty starttime; + private StringProperty endtime; + private StringProperty attachment; + //通知发布者的名字 + private StringProperty username; + + public SingleNotice() { + // TODO Auto-generated constructor stub + noticeId=new SimpleIntegerProperty(); + title=new SimpleStringProperty(); + content=new SimpleStringProperty(); + starttime=new SimpleStringProperty(); + endtime=new SimpleStringProperty(); + attachment=new SimpleStringProperty(); + username=new SimpleStringProperty(); + } + +// //为listview而写的方法,用于同步显示在前端 +// @Override +// public String toString(){ +// return (username+": "+title); +// } + + //noticeId + public Integer getNoticeId() { + return noticeId.get(); +} + public void setNoticeId(Integer msgId) { + this.noticeId.set(msgId); + } + public IntegerProperty noticeIdProperty() { + return noticeId; + } + + //username + public void setUsername(String username) { + this.username.set(username); + } + public String getUsername() { + return username.get(); + } + public StringProperty usernameProperty(){ + return username; + } + + //title + public void setTitle(String title) { + this.title.set(title); + } + public String getTitle() { + return title.get(); + } + //显示标题全部内容 + public String getSimpleTitle() { + return title.get(); + } + public StringProperty titleProperty(){ + return title; + } + + //content + public void setContent(String content) { + this.content.set(content); + } + public String getContent() { + return content.get(); + } + public StringProperty contentProperty(){ + return content; + } + + //starttime + public void setStarttime(String starttime) { + this.starttime.set(starttime); + } + public String getStarttime() { + return starttime.get(); + } + public StringProperty starttimeProperty(){ + return starttime; + } + + //endtime + public void setEndtime(String endtime) { + this.endtime.set(endtime); + } + public String getEndtime() { + return endtime.get(); + } + public StringProperty endtimeProperty(){ + return endtime; + } + + //attachment + public void setAttachment(String attachment) { + this.attachment.set(attachment); + } + public String getAttachment() { + return attachment.get(); + } + public StringProperty attachmentProperty(){ + return attachment; + } +} diff --git a/src/main/java/cn/minoa/util/AESCrypto.java b/src/main/java/cn/minoa/util/AESCrypto.java new file mode 100644 index 0000000..ab837c1 --- /dev/null +++ b/src/main/java/cn/minoa/util/AESCrypto.java @@ -0,0 +1,143 @@ +package cn.minoa.util; + +import org.apache.commons.codec.digest.DigestUtils; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import javax.crypto.Cipher; + +public class AESCrypto{ + + public static String encryptBasedAES(String content) { +// content= URLEncoder.encode(content, StandardCharsets.UTF_8); + byte[] encrypt=encryptBasedAES(content,"123"); + return ParseSystemUtil.parseByte2HexStr(encrypt); + } + + + /** + * AES加密字符串 + * + * @param content 需要被加密的字符串 + * @param password 加密需要的密码 + * @return 密文 + */ + public static byte[] encryptBasedAES(String content, String password) { +// content= URLEncoder.encode(content, StandardCharsets.UTF_8); +// password= URLEncoder.encode(password, StandardCharsets.UTF_8); + try { + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 +// kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出 + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行 + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 + Cipher cipher = Cipher.getInstance("AES");// 创建密码器 + byte[] byteContent = content.getBytes(StandardCharsets.UTF_8); + cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 + byte[] result = cipher.doFinal(byteContent);// 加密 + return result; + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + return null; + } + + public static String decryptBasedAES(String hexStr) { +// hexStr= URLEncoder.encode(hexStr, StandardCharsets.UTF_8); + byte[] byte2 = ParseSystemUtil.parseHexStr2Byte(hexStr); + String s=new String(decryptBasedAES(byte2, "123"),StandardCharsets.UTF_8); +// s=URLEncoder.encode(s,StandardCharsets.UTF_8); + return s; + } + +// public static byte[] decryptBasedAES(byte[] content) { +// return decryptBasedAES(content, "123"); +// } + + /** + * 解密AES加密过的字符串 + * + * @param content AES加密过过的内容 + * @param password 加密时的密码 + * @return 明文 + */ + public static byte[] decryptBasedAES(byte[] content, String password){ +// password=URLEncoder.encode(password,StandardCharsets.UTF_8); + try { + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 +// kgen.init(128, new SecureRandom(password.getBytes())); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 + Cipher cipher = Cipher.getInstance("AES");// 创建密码器 + cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 + byte[] result = cipher.doFinal(content); + return result; // 明文 + } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException e) { + e.printStackTrace(); + } + return null; + } + + // 根据密码,生成一个密钥 + public static byte[] creatAESKey(){ + return creatAESKey("123"); + } + + // 根据密码,生成一个16字节的AES密钥 + public static byte[] creatAESKey(String password){ + try{ + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 + // kgen.init(128, new SecureRandom(password.getBytes())); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 + System.out.println("AES密码:"+password); + System.out.println("根据密码生成的AES密钥长度:"+enCodeFormat.length); + return enCodeFormat; + }catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } + + // 测试主函数 + public static void main(String[] args) throws Exception { + String password="123"; + creatAESKey(password); + +// String content = "哈哈哈"; +// System.out.println("需要加密的内容:" + content); +// String enc=encryptBasedAES(content); +// System.out.println("加密后的16进制密文:" + enc); +// String dec=decryptBasedAES(enc); +// System.out.println("解密后的内容:" + dec); + } + +} diff --git a/src/main/java/cn/minoa/util/Aes.java b/src/main/java/cn/minoa/util/Aes.java new file mode 100644 index 0000000..f2f417d --- /dev/null +++ b/src/main/java/cn/minoa/util/Aes.java @@ -0,0 +1,270 @@ +package cn.minoa.util; + +import cn.minoa.MainApp; +import net.named_data.jndn.Face; +import net.named_data.jndn.Interest; +import net.named_data.jndn.Name; +import net.named_data.jndn.util.Blob; + +import javax.crypto.Cipher ; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Random; + +public class Aes { + + // 加密String + public static String Encrypt(String sSrc, String sKey) throws Exception { + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式" + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(sSrc.getBytes(StandardCharsets.UTF_8)); + + Base64.Encoder encoder= Base64.getMimeEncoder(); + + return encoder.encodeToString(encrypted);//此处使用BASE64做转码功能,同时能起到2次加密的作用。 + // return "new Base64().encodeToString(encrypted)";//此处使用BASE64做转码功能,同时能起到2次加密的作用。 + } + + // 去掉base64编解码的加密 + public static byte[] newEncrypt(String sSrc, String sKey) throws Exception { + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式" + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(sSrc.getBytes(StandardCharsets.UTF_8)); + + return encrypted; + } + + // 去掉base64编解码及new String操作的纯加密 + public static byte[] pureEncrypt(byte[] sSrc, String sKey) throws Exception { + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式" + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(sSrc); + + return encrypted; + } + + // 解密String + public static String Decrypt(String sSrc, String sKey) throws Exception { + try { + // 判断Key是否正确 + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + Base64.Decoder decoder = Base64.getMimeDecoder() ; + + byte[] encrypted1 = decoder.decode(sSrc); + + byte[] original = cipher.doFinal(encrypted1); + String originalString = new String(original, StandardCharsets.UTF_8); + return originalString; + } catch (Exception ex) { + System.out.println(ex.toString()); + return null; + } + } + + // 去掉base64编解码的解密 + public static String newDecrypt(byte[] sSrc, String sKey) throws Exception { + try { + // 判断Key是否正确 + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + + byte[] original = cipher.doFinal(sSrc); + String originalString = new String(original, StandardCharsets.UTF_8); + return originalString; + } catch (Exception ex) { + System.out.println(ex.toString()); + return null; + } + } + + // 去掉base64编解码及new String操作的纯解密 + public static byte[] pureDecrypt(byte[] sSrc, String sKey) throws Exception { + try { + // 判断Key是否正确 + if (sKey == null) { + System.out.print("Key为空null"); + return null; + } + // 判断Key是否为16位 + if (sKey.length() != 16) { + System.out.print("Key长度不是16位"); + return null; + } + byte[] raw = sKey.getBytes(StandardCharsets.UTF_8); + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + + byte[] original = cipher.doFinal(sSrc); + return original; + } catch (Exception ex) { + System.out.println(ex.toString()); + return null; + } + } + + // 使用默认密钥加密String + public static String Encrypt(String sSrc) throws Exception { + return Encrypt(sSrc,getDefaultKey()); + } + + + // 使用默认密钥解密String + public static String Decrypt(String sSrc) throws Exception { + return Decrypt(sSrc,getDefaultKey()); + } + + + // 获取默认AES密钥 + public static String getDefaultKey() throws Exception { +// return "1234567890abcdef"; + return MainApp.aesKey_16Byte; + } + + + /** + * + * @param length 需要的字符长度 + * @return 返回随机字母数字组合字符串 + */ + public static String getRandomString(int length) { + String val = ""; + Random random = new Random(); + + //参数length,表示生成几位随机数 + for (int i = 0; i < length; i++) { + // 随机数由0-9,a-z,A-Z组成,数字占10个,字母占52个,数字、字母占比1:5(标准的应该是10:52) + // random.nextInt(6) 0-5中6个数取一个 + String charOrNum = (random.nextInt(6) + 6) % 6 >= 1 ? "char" : "num"; + //输出字母还是数字 + if ("char".equalsIgnoreCase(charOrNum)) { + //输出是大写字母还是小写字母,输出比例为1:1 + int temp = random.nextInt(2) % 2 == 0 ? 97 : 65; + //char(65)-char(90) 为大写字母A-Z;char(97)-char(122)为小写字母a-z + val += (char) (random.nextInt(26) + temp); + } else if ("num".equalsIgnoreCase(charOrNum)) { + val += String.valueOf(random.nextInt(10)); + } + } + + return val; + } + + static public void main(String args[]){ + Aes aes = new Aes(); + /* + * 此处使用AES-128-ECB加密模式,key需要为16位。 + */ + try { + String cKey = "1234567890abcdef"; + // 需要加密的字串 + String cSrc = "寻寻觅觅,冷冷清清,凄凄惨惨戚戚。乍暖还寒时候,最难将息。三杯两盏淡酒,怎敌他、晚来风急!雁过也,正伤心,却是旧时相识。\n" + + "\n" + + "满地黄花堆积,憔悴损,如今有谁堪摘?守着窗儿,独自怎生得黑!梧桐更兼细雨,到黄昏、点点滴滴。这次第,怎一个愁字了得!"; + System.out.println(cSrc); + // 加密 + String enString = aes.Encrypt(cSrc, cKey); + System.out.println("加密后的字串是:" + enString); + // 解密 + String DeString = aes.Decrypt(enString, cKey); + System.out.println("解密后的字串是:" + DeString); + + String cEncodeStr = "m7yOVWlBAFFOqD3bHi/yPxUgLvR4nFmAWDHyMqLXG4Fynzv3Nu/KGKYhXaWQxnT8B1Xc4/FmM2UPLxurPLW4aaTWxiB8WltJidi53wEsaz+tZuShv7apMfFwo6848A2IKC/pN33eSfiA8DeGK03xp9BLKd0yh2rlCi/5owelkVUmkfodK5RHyAkbhTXgLSvO"; + String deStr = aes.Decrypt(cEncodeStr,cKey); + System.out.println("hello:" + deStr); + + Face face = new Face("121.15.171.89") ; +// Consumer1 consumer1 = new Consumer1(); + Interest interest = new Interest(new Name("/ndn/edu/pkusz/OA/aesTest")) ; + interest.setMustBeFresh(true) ; + + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(enString))) ; + System.out.println("Express interest : " + interest.getName().toUri()); + System.out.println("parameter size = " + interest.getApplicationParameters().size()); + + System.out.println(interest.getName().toUri()); + face.expressInterest(interest, (interest1, data) -> { + System.out.println("onData : " + data.getName()); + Base64.Encoder base64Encoder = Base64.getMimeEncoder(); + + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = aes.Decrypt(dataContentBase64,"1234567890abcdef") ; + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("data =========== : " + plainText); + }); + while (true) { + face.processEvents(); + Thread.sleep(1000); + //System.out.println("========================================="); + } + + }catch (Exception e){ + + } + + } +} diff --git a/src/main/java/cn/minoa/util/DateUtil.java b/src/main/java/cn/minoa/util/DateUtil.java new file mode 100644 index 0000000..70826d6 --- /dev/null +++ b/src/main/java/cn/minoa/util/DateUtil.java @@ -0,0 +1,34 @@ +package cn.minoa.util; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +public class DateUtil { + private static final String DATE_PATTERN="dd.MM.yyyy"; + + private static final DateTimeFormatter DATE_FORMATTER= + DateTimeFormatter.ofPattern(DATE_PATTERN); + +// 时间转字符串 + public static String format(LocalDate date) { + if(date==null) { + return null; + } + return DATE_FORMATTER.format(date); + } + +// 字符串转时间 + public static LocalDate parse(String dateString) { + try { + return DATE_FORMATTER.parse(dateString,LocalDate::from); + }catch(DateTimeParseException e) { + return null; + } + } + +// 判断字符串是否是一个有效日期 + public static boolean validDate(String dateString) { + return DateUtil.parse(dateString)!=null; + } +} diff --git a/src/main/java/cn/minoa/util/ECOSUtil.java b/src/main/java/cn/minoa/util/ECOSUtil.java new file mode 100644 index 0000000..fbb405a --- /dev/null +++ b/src/main/java/cn/minoa/util/ECOSUtil.java @@ -0,0 +1,175 @@ +package cn.minoa.util; +// 发送ECOS拟态存储有关的信号 + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.*; +import net.named_data.jndn.Interest; +import net.named_data.jndn.Name; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.util.Blob; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; + +public class ECOSUtil { + // 上传文件结束开始,发送一个信号给后台,以通知ECOS该信息 + public static void sendUploadFileBeginSignal(String filename) { +// String signalString= DownAndUpLoadData.getUploadFile(filename,(long)-2); + String signalString = getECOSUploadJson("/ecos_begin", filename, (long) 0); + // 发送数据请求 + Long seqLong = MainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(signalString); + System.out.println("ECOS 信号: " + orderInfo.getJsonString()); + MainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo); + // 获取用户列表信息 + ResponseData responseData = MainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); +// System.out.println("getUsers-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + System.out.println("ECOS failed to receive file upload begin signal."); + } + if (codeInteger == 200) { + System.out.println("ECOS received file upload begin signal successfully."); + } else { + System.out.println(jsonObject.toString()); + System.out.println("ECOS failed to receive file upload begin signal."); + } + } + + // 上传文件结束,发送一个信号给后台,以通知ECOS该信息 + public static void sendUploadFileEndSignal(String filename) { +// String signalString= DownAndUpLoadData.getUploadFile(filename,(long)-1); + String signalString = getECOSUploadJson("/ecos_end", filename, (long) 0); + // 发送数据请求 + Long seqLong = MainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(signalString); + System.out.println("ECOS 信号: " + orderInfo.getJsonString()); + MainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo); + // 获取用户列表信息 + ResponseData responseData = MainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); +// System.out.println("getUsers-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + System.out.println("ECOS failed to receive file upload end signal."); + } + if (codeInteger == 200) { + System.out.println("ECOS received file upload end signal successfully."); + } else { + System.out.println(jsonObject.toString()); + System.out.println("ECOS failed to receive file upload end signal."); + } + } + + /** + * 发送一个文件上传结束的信号(ECOS) + */ + public static void sendECOSUploadEndSignal(String filename) { + String ecosprefix = "/uploadFile"; + String ecosEndUploadString = "ecos"; + OrderInfo orderInfo = new OrderInfo(); + Long seqLong = MainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + String testStr = DownAndUpLoadData.getUploadFile(filename, (long) -1); + //System.out.println("*******************:" + testStr + "*******************"); + orderInfo.setJsonString(testStr); + + // 获取json格式的数据请求字符串 + //String requestStr = MinoaDataAPI.getRequestStr(orderPrefix, orderInfo); + + // 发送数据兴趣包 + Name name = new Name(MinoaDataAPI.globalPrefixString + ecosprefix + "/" + seqLong); + Interest interest = new Interest(name); + interest.setMustBeFresh(true); + try { + // 构建应用参数 + byte[] requestByte = null; + int readLen = StringByteLengthUtil.getByteLength(ecosEndUploadString); + int jsonLen = StringByteLengthUtil.getByteLength(orderInfo.getJsonString()); + requestByte = new byte[jsonLen + 1 + readLen]; + System.arraycopy(orderInfo.getJsonString().getBytes("utf-8"), 0, requestByte, 0, jsonLen); + requestByte[jsonLen] = 0; + System.arraycopy(ecosEndUploadString.getBytes("utf-8"), 0, requestByte, jsonLen + 1, readLen); + // 赋值兴趣包的附带信息,并标志区块链 + interest.setApplicationParameters(new Blob(requestByte)); + //超时时长设为2s + interest.setInterestLifetimeMilliseconds(2000); + // 密钥“黑盒” + try { + KeyManager.INSTANCE.getKeyChain().sign(interest); + } catch (PibImpl.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (KeyChain.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (net.named_data.jndn.security.tpm.TpmBackEnd.Error e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // 使用tcp发送数据请求兴趣包 + MainApp.minoaDataAPI.executeOrder(ecosprefix, interest, seqLong); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + e.printStackTrace(); + } + // 解析应答数据 + ResponseData responseData = MainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); +// System.out.println("getUsers-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + System.out.println("ECOS failed to receive file upload end signal."); + } + if (codeInteger == 200) { + System.out.println("ECOS received file upload end signal successfully."); + } else { + System.out.println(jsonObject.toString()); + System.out.println("ECOS failed to receive file upload end signal."); + } + } + + + public static String getECOSUploadJson(String prefix, String filename, Long offset) { + JSONObject jsonObject = new JSONObject(); + String commandString = prefix; + String usernameString = MainApp.loginer.getUserName(); + String uuidString = MainApp.loginer.getUuid(); + String filenameString = filename; + Long offsetInteger = offset; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("offset", offsetInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} diff --git a/src/main/java/cn/minoa/util/EncryptionUtil.java b/src/main/java/cn/minoa/util/EncryptionUtil.java new file mode 100644 index 0000000..cfe0564 --- /dev/null +++ b/src/main/java/cn/minoa/util/EncryptionUtil.java @@ -0,0 +1,176 @@ +package cn.minoa.util; + +import org.apache.commons.codec.digest.DigestUtils; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import javax.crypto.Cipher; + +public class EncryptionUtil { + // 获取文件md5加密结果 + public static String getFileHash(String path){ + Long startTime = System.currentTimeMillis(); + // File +// String path="E:\\系统镜像\\ubuntu-18.04.3-desktop-amd64.iso"; +// String path="E:\\系统镜像\\UltraISO.9.7.0.3476.exe"; + String res=null; + try { + res= DigestUtils.md5Hex(new FileInputStream(path)); + System.out.println("文件"+path+"加密结果:"+res); + } catch (IOException e) { + e.printStackTrace(); + } + Long endTime=System.currentTimeMillis(); + System.out.println("文件"+path+"加密耗时:"+(endTime-startTime)+"ms"); + return res; + } + + public static String getMD5String(String str) { + str= URLEncoder.encode(str, StandardCharsets.UTF_8); + try { + // 生成一个MD5加密计算摘要 + MessageDigest md = MessageDigest.getInstance("MD5"); + // 计算md5函数 + md.update(str.getBytes(StandardCharsets.UTF_8)); + // digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符 + // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值 + //一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方) + String res = new BigInteger(1, md.digest()).toString(16); + System.out.println(str + " MD5加密结果: " + res); + return res; + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + public static String encryptBasedAES(String content) { +// content= URLEncoder.encode(content, StandardCharsets.UTF_8); + byte[] encrypt=encryptBasedAES(content,"123"); + return ParseSystemUtil.parseByte2HexStr(encrypt); + } + +// public static byte[] encryptBasedAES(String content) { +// return encryptBasedAES(content, "123"); +// } + + /** + * AES加密字符串 + * + * @param content 需要被加密的字符串 + * @param password 加密需要的密码 + * @return 密文 + */ + public static byte[] encryptBasedAES(String content, String password) { +// content= URLEncoder.encode(content, StandardCharsets.UTF_8); +// password= URLEncoder.encode(password, StandardCharsets.UTF_8); + try { + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 +// kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出 + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行 + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 + Cipher cipher = Cipher.getInstance("AES");// 创建密码器 + byte[] byteContent = content.getBytes(StandardCharsets.UTF_8); + cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 + byte[] result = cipher.doFinal(byteContent);// 加密 + return result; + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + return null; + } + + public static String decryptBasedAES(String hexStr) { +// hexStr= URLEncoder.encode(hexStr, StandardCharsets.UTF_8); + byte[] byte2 = ParseSystemUtil.parseHexStr2Byte(hexStr); + String s=new String(decryptBasedAES(byte2, "123"),StandardCharsets.UTF_8); +// s=URLEncoder.encode(s,StandardCharsets.UTF_8); + return s; + } + +// public static byte[] decryptBasedAES(byte[] content) { +// return decryptBasedAES(content, "123"); +// } + + /** + * 解密AES加密过的字符串 + * + * @param content AES加密过过的内容 + * @param password 加密时的密码 + * @return 明文 + */ + public static byte[] decryptBasedAES(byte[] content, String password){ +// password=URLEncoder.encode(password,StandardCharsets.UTF_8); + try { + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 +// kgen.init(128, new SecureRandom(password.getBytes())); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 + SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 + Cipher cipher = Cipher.getInstance("AES");// 创建密码器 + cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 + byte[] result = cipher.doFinal(content); + return result; // 明文 + } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException e) { + e.printStackTrace(); + } + return null; + } + + + // 根据密码,生成一个密钥 + public static void creatAESKey(String password){ + try{ + KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 + // kgen.init(128, new SecureRandom(password.getBytes())); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes(StandardCharsets.UTF_8)); + kgen.init(128, secureRandom); + SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 + byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 + System.out.println("AES密码:"+password); + System.out.println("根据密码生成的AES密钥长度:"+enCodeFormat.length); + }catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + // 测试主函数 + public static void main(String[] args) throws Exception { + String password="12345678"; + creatAESKey(password); + +// String content = "哈哈哈"; +// System.out.println("需要加密的内容:" + content); +// String enc=encryptBasedAES(content); +// System.out.println("加密后的16进制密文:" + enc); +// String dec=decryptBasedAES(enc); +// System.out.println("解密后的内容:" + dec); + } + +} diff --git a/src/main/java/cn/minoa/util/FileBPRUtil.java b/src/main/java/cn/minoa/util/FileBPRUtil.java new file mode 100644 index 0000000..aaaf983 --- /dev/null +++ b/src/main/java/cn/minoa/util/FileBPRUtil.java @@ -0,0 +1,220 @@ +package cn.minoa.util; + +import cn.minoa.model.BPRInfo; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +// 文件断点续传支持类 +// 功能:存储和移除传输失败的文件信息 +public class FileBPRUtil { + + // 判断某md5哈希值是否在本地存储条目中 + public static boolean isHaveUploadBreakPointInfo(String hashString) { + // 加密 + hashString=EncryptionUtil.encryptBasedAES(hashString); + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream("." + File.separator + "ndn" + File.separator + "FileBPRenewal.properties")); + if (inputStream == null) { + System.out.println("没有找到断点续传配置文件..."); + return false; + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + String filename=settingProperties.getProperty("fileName-"+hashString); + System.out.println(hashString+" : "+filename); + if(filename==null){ + return false; + }else{ + System.out.println("本地存在该文件的断点信息:"+hashString); + return true; + } + } catch (IOException e) { +// e.printStackTrace(); + return false; + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + +// public static boolean isHaveDownloadBreakPointInfo(String fileUrlString) { +// return isHaveUploadBreakPointInfo(fileUrlString); +// } + + + // 存储上传文件断点信息到本地 + public static void setUploadBreakPointInfo(String hashString, + String pureFileName, + String serverPath, + Long point){ + // 加密 + hashString=EncryptionUtil.encryptBasedAES(hashString); + pureFileName=EncryptionUtil.encryptBasedAES(pureFileName); + serverPath=EncryptionUtil.encryptBasedAES(serverPath); + String pointString=EncryptionUtil.encryptBasedAES(point.toString()); + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream("."+ File.separator+"ndn"+File.separator+"FileBPRenewal.properties")); + if (inputStream == null) { + System.out.println("没有找到断点续传配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty("fileName-"+hashString, pureFileName); + settingProperties.setProperty("fileServerPath-"+hashString,serverPath); + settingProperties.setProperty("filePoint-"+hashString, pointString); + outputStream=new BufferedOutputStream(new FileOutputStream("."+ File.separator+"ndn"+File.separator+"FileBPRenewal.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"commit changes"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // 移除上传文件的断点信息 + public static void deleteUploadBreakPointInfo(String hashString){ + // 加密 + hashString=EncryptionUtil.encryptBasedAES(hashString); + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream("."+ File.separator+"ndn"+File.separator+"FileBPRenewal.properties")); + if (inputStream == null) { + System.out.println("没有找到断点续传配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.remove("fileName-"+hashString); + settingProperties.remove("fileServerPath-"+hashString); + settingProperties.remove("filePoint-"+hashString); + outputStream=new BufferedOutputStream(new FileOutputStream("."+ File.separator+"ndn"+File.separator+"FileBPRenewal.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"commit changes"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + +// public static void setDownloadBreakPointInfo(String fileUrlString, +// String pureFileName, +// String localPath, +// Long point){ +// setUploadBreakPointInfo(fileUrlString,pureFileName,localPath,point); +// } +// +// public static void deleteDownloadBreakPointInfo(String fileUrlString){ +// deleteUploadBreakPointInfo(fileUrlString); +// } + + // 取出上传文件断点信息到本地 + public static BPRInfo getUploadBreakPointInfo(String hashString) { + // 加密 + hashString=EncryptionUtil.encryptBasedAES(hashString); + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream("." + File.separator + "ndn" + File.separator + "FileBPRenewal.properties")); + if (inputStream == null) { + System.out.println("没有找到断点续传配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + BPRInfo bprInfo=new BPRInfo(); + bprInfo.setFileId(hashString); + bprInfo.setFileName(EncryptionUtil.decryptBasedAES(settingProperties.getProperty("fileName-"+hashString))); + bprInfo.setFileLocator(EncryptionUtil.decryptBasedAES(settingProperties.getProperty("fileServerPath-"+hashString))); + bprInfo.setPoint(Long.parseLong(EncryptionUtil.decryptBasedAES(settingProperties.getProperty("filePoint-"+hashString)))); + System.out.println("util-文件名:"+bprInfo.getFileName()); + System.out.println("util-文件断点:"+bprInfo.getPoint()); + System.out.println("util-文件路径:"+bprInfo.getFileLocator()); + if((bprInfo.getFileName()==null)||(bprInfo.getPoint()==null)||(bprInfo.getFileLocator()==null)){ + return null; + } + return bprInfo; + } catch (IOException e) { + e.printStackTrace(); + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + +// public static BPRInfo getDownloadBreakPointInfo(String fileUrlString) { +// return getUploadBreakPointInfo(fileUrlString); +// } + +} + + +//1. 上传文件 +// 1.1 如果上传成功,且该次上传是续传,则移除该文件哈希值对应的本地存储条目; +// 1.2 如果上传失败,则将文件断点信息存到本地:md5哈希值、纯文件名、服务器路径、断点 +// 1.3 每次上传文件,都要计算一下md5哈希值,然后查表,看本地有没有这个哈希值。 +// 如果有,就取文件名、要传的服务器路径,对比一下,全部都对的上,就从断点位置继续传该文件;如果没该哈希值,或文件名、路径不一致,则从0开始上传。 +// +//2. 下载文件 +// 2.1 如果下载成功,且该次下载是续传,则移除该次下载的文件对应的服务器url所对应的本地存储条目; +// 2.2 如果下载失败,则将文件断点信息保存到本地:文件对应的服务器url(如:/a/b.txt)、纯文件名、本地目录、断点 +// 2.3 每次下载文件,都查看要下载的文件的服务器url是否在本地存储中。如果在,就取纯文件名、本地目录对比一下,如果全部都对的上,就从断点位置继续传该文件;如果没该服务器url,或文件名、路径不一致,则从0开始下载。 +//3. 问题: +// 1. 由于上述断点全部存储在客户端,那么对于电脑突然死机或用户直接关闭客户端的情况,是无法将断点信息保存到本地的,也就无法续传; +// 2. 上述下载中,所谓文件服务器URL,并不是文件唯一标识,如果出现文件错判,续传之后的文件将可能是两个不同文件的结合体。 + diff --git a/src/main/java/cn/minoa/util/FileUtil.java b/src/main/java/cn/minoa/util/FileUtil.java new file mode 100644 index 0000000..acea801 --- /dev/null +++ b/src/main/java/cn/minoa/util/FileUtil.java @@ -0,0 +1,47 @@ +package cn.minoa.util; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class FileUtil { + + /** + * 格式化时间 + * @param format 格式化格式,基础格式为yyyy-MM-dd HH:mm:ss + * @param time 精确到ms + * @return + */ + public static String formatTime(String format, long time) + { + SimpleDateFormat df = new SimpleDateFormat(format); + return df.format(new Date(time)); + } + + // 后台返回的Long类型时间戳(精确到s)转日期 + public static String timeToDateString(Long time){ + String timeString = formatTime("yyyy-MM-dd HH:mm:ss", time*1000); + System.out.println(timeString); + return timeString; + } + + // 文件大小根据具体大小,转成B/KB/M/G/T + public static String sizeToShowSize(Long size){ + if(size<1024){ + return size+" B"; + } + Double sizeKB=size/1024.0; + if(sizeKB<1024){ + return String.format("%.3f",sizeKB)+" KB"; + } + Double sizeM=sizeKB/1024.0; + if(sizeM<1024){ + return String.format("%.3f",sizeM)+" M"; + } + Double sizeG=sizeM/1024.0; + if(sizeG<1024){ + return String.format("%.3f",sizeG)+" G"; + } + Double sizeT=sizeG/1024.0; + return String.format("%.3f",sizeT)+" T"; + } +} diff --git a/src/main/java/cn/minoa/util/GenerateSettingFiles.java b/src/main/java/cn/minoa/util/GenerateSettingFiles.java new file mode 100644 index 0000000..37893e7 --- /dev/null +++ b/src/main/java/cn/minoa/util/GenerateSettingFiles.java @@ -0,0 +1,144 @@ +package cn.minoa.util; +// 生成配置文件 + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +public class GenerateSettingFiles { + + public static void generateInitFiles(){ + generateNDNDir(); + generateSettingFile(); + generateBPRFile(); + generateNDNBlockFile(); + } + + public static void generateNDNDir(){ + String path="." + File.separator + "ndn"; + File file=new File(path); + if(!file.exists()&&!file.isDirectory()){ + file.mkdir(); + System.out.println("ndn目录创建成功"); + }else{ + System.out.println("ndn目录已经存在"); + } + } + + public static void generateNDNBlockFile() { + String path = "." + File.separator + "ndn" + File.separator +"BlockSuccessUsers.properties"; + generateNDNBlockFile(path); + } + + public static void generateNDNBlockFile(String path) { + File file = new File(path); + // 创建配置文件 + if (!file.exists()) { + try { + file.createNewFile(); + System.out.println("区块链注册信息文件创建成功"); + } catch (IOException e) { + System.out.println("区块链注册信息文件创建失败:" + e.getMessage()); + } + } else { + System.out.println(path + "区块链注册信息文件已经存在"); + } + } + + public static void generateBPRFile() { + String path = "." + File.separator + "ndn" + File.separator +"FileBPRenewal.properties"; + generateBPRFiles(path); + } + + public static void generateBPRFiles(String path) { + File file = new File(path); + // 创建配置文件 + if (!file.exists()) { + try { + file.createNewFile(); + System.out.println("断点信息记录文件创建成功"); + } catch (IOException e) { + System.out.println("断点信息记录文件创建失败:" + e.getMessage()); + } + } else { + System.out.println(path + "断点信息记录文件已经存在"); + } + } + + public static void generateSettingFile() { + String path = "." + File.separator + "ndn" + File.separator + "settings.properties"; + generateSettingFile(path); + } + + public static void generateSettingFile(String path) { + File file = new File(path); + boolean isNew=false; //标志配置文件是否是新建的 + // 创建配置文件 + if (!file.exists()) { + isNew=true; + try { + file.createNewFile(); + System.out.println("setting配置文件创建成功"); + } catch (IOException e) { + System.out.println("setting配置文件创建失败:" + e.getMessage()); + } + } else { + isNew=false; + System.out.println(path + "配置文件已经存在"); + } + // 如果是新建配置文件,则写入配置文件的默认内容 + if(isNew) { + InputStream inputStream = null; + OutputStream outputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream(path)); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + // 解决中文乱码问题 + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty("ServerIPAddress", "116.77.74.139"); + settingProperties.setProperty("ServerPortAddress", "6363"); + settingProperties.setProperty("ndnBlockChainIPAddress", "116.77.74.139"); + settingProperties.setProperty("ndnBlockChainPortAddress", "9001"); + settingProperties.setProperty("DataPackagePrefix", "/ndn/edu/pkusz/OA"); + settingProperties.setProperty("ThreadNumberForFileTransfer", "5"); + settingProperties.setProperty("KeyName", "/default"); + settingProperties.setProperty("LoginStatus", "false"); + settingProperties.setProperty("loginUserName", "default"); + settingProperties.setProperty("loginPassWord", "default"); + outputStream = new BufferedOutputStream(new FileOutputStream(path)); + // 解决中文乱码问题 + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); + settingProperties.store(bw, "初始化配置文件"); + System.out.println("setting配置文件内容初始化成功"); + } catch (IOException e) { +// e.printStackTrace(); + System.out.println("setting配置文件内容填充失败" + e.getMessage()); + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + }else{ + System.out.println(path+"配置文件内容保持不变"); + } + } + +} diff --git a/src/main/java/cn/minoa/util/IOUtils.java b/src/main/java/cn/minoa/util/IOUtils.java new file mode 100644 index 0000000..c5fb330 --- /dev/null +++ b/src/main/java/cn/minoa/util/IOUtils.java @@ -0,0 +1,54 @@ +package cn.minoa.util; +import java.io.*; + +/** + * description: IO 工具类, 读写文件 + * create: https://www.jianshu.com/p/87d2148a4298 + **/ +public class IOUtils { + + static void writeFile(String data, File file) throws IOException { + OutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(data.getBytes()); + out.flush(); + } finally { + close(out); + } + } + + public static String readFile(File file) throws IOException { + InputStream in = null; + ByteArrayOutputStream out = null; + try { + in = new FileInputStream(file); + out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + int len = -1; + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + out.flush(); + byte[] data = out.toByteArray(); + String res=new String(data); + System.out.println("publicKey in file: "+res); + System.out.println("publicKey length in file: "+res.length()); + return new String(data); + } finally { + close(in); + close(out); + } + } + + public static void close(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + // nothing + } + } + } + +} diff --git a/src/main/java/cn/minoa/util/KeyStorageUtil.java b/src/main/java/cn/minoa/util/KeyStorageUtil.java new file mode 100644 index 0000000..db6f076 --- /dev/null +++ b/src/main/java/cn/minoa/util/KeyStorageUtil.java @@ -0,0 +1,81 @@ +package cn.minoa.util; + +import cn.minoa.MainApp; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +public class KeyStorageUtil { + + // 存数据 + public static void setKeyName(String keyName){ + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty("KeyName", keyName); + outputStream=new BufferedOutputStream(new FileOutputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"修改配置文件"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // 取数据 + public static String getKeyName() { + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream("." + File.separator + "ndn" + File.separator + + "settings.properties")); +// inputStream = MainApp.class.getClassLoader().getResourceAsStream("/ndn/settings.properties"); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + String keyName = settingProperties.getProperty("KeyName"); + return keyName; + } catch (IOException e) { + e.printStackTrace(); + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + +} diff --git a/src/main/java/cn/minoa/util/LocalDateAdapter.java b/src/main/java/cn/minoa/util/LocalDateAdapter.java new file mode 100644 index 0000000..6876b2b --- /dev/null +++ b/src/main/java/cn/minoa/util/LocalDateAdapter.java @@ -0,0 +1,16 @@ +package cn.minoa.util; + +//import java.time.LocalDate; +//import javax.xml.bind.annotation.adapters.XmlAdapter; + +//使得JAXB可以转换日期到XML +//public class LocalDateAdapter extends XmlAdapter { +// +// public LocalDate unmarshal(String v) throws Exception{ +// return LocalDate.parse(v); +// } +// +// public String marshal(LocalDate v) throws Exception{ +// return v.toString(); +// } +//} diff --git a/src/main/java/cn/minoa/util/LoggerUtil.java b/src/main/java/cn/minoa/util/LoggerUtil.java new file mode 100644 index 0000000..f4e8f9d --- /dev/null +++ b/src/main/java/cn/minoa/util/LoggerUtil.java @@ -0,0 +1,33 @@ +package cn.minoa.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; + +public class LoggerUtil { + + // 将文件名、文件操作、传输耗时等信息写入日志. + public static void insertFileAction(String filename, String fileaction, String usedtime) { + try { + File file = new File("." + File.separator + "ndn" + + File.separator +"log.txt"); + FileOutputStream fos = null; + if (!file.exists()) { + file.createNewFile();//如果文件不存在,就创建该文件 + fos = new FileOutputStream(file);//首次写入获取 + } else { + //如果文件已存在,那么就在文件末尾追加写入 + fos = new FileOutputStream(file, true);//这里构造方法多了一个参数true,表示在文件末尾追加写入 + } + + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");//指定以UTF-8格式写入文件 + osw.write("文件名:"+filename+" 文件操作:"+fileaction+" 文件耗时:"+usedtime); + osw.write("\r\n"); + //写入完成关闭流 + osw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/cn/minoa/util/ParseSystemUtil.java b/src/main/java/cn/minoa/util/ParseSystemUtil.java new file mode 100644 index 0000000..c398c85 --- /dev/null +++ b/src/main/java/cn/minoa/util/ParseSystemUtil.java @@ -0,0 +1,39 @@ +package cn.minoa.util; + +/** + * 进制转换工具类 + */ +public class ParseSystemUtil { + + /**将二进制转换成16进制 + * @param buf + * @return + */ + public static String parseByte2HexStr(byte buf[]) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < buf.length; i++) { + String hex = Integer.toHexString(buf[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + sb.append(hex.toUpperCase()); + } + return sb.toString(); + } + + /**将16进制转换为二进制 + * @param hexStr + * @return + */ + public static byte[] parseHexStr2Byte(String hexStr) { + if (hexStr.length() < 1) + return null; + byte[] result = new byte[hexStr.length()/2]; + for (int i = 0;i< hexStr.length()/2; i++) { + int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); + int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); + result[i] = (byte) (high * 16 + low); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/cn/minoa/util/RSACrypto.java b/src/main/java/cn/minoa/util/RSACrypto.java new file mode 100644 index 0000000..c742be3 --- /dev/null +++ b/src/main/java/cn/minoa/util/RSACrypto.java @@ -0,0 +1,149 @@ +package cn.minoa.util; + +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import javax.crypto.Cipher; + +//import utilcode.; +//import com.blankj.utilcode.util.EncryptUtils; +// utilcode: https://blog.csdn.net/a940659387/article/details/82116556?utm_source=blogxgwz5 + +// RSA算法实现 +// 原博文:https://blog.csdn.net/super_cui/article/details/70821268 +// 字符串转公钥:https://blog.csdn.net/petib_wangwei/article/details/37601081 + +public class RSACrypto { + private final static String RSA = "RSA"; +// public static PublicKey uk; +// public static PrivateKey rk; + + private static PublicKey getPubKey() { + String pubKey= "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UIWR510Jlwk6y287DfW" + + "WpsCxafpVr2QLeqAaR04qOaaaqXuhZbUoaxgS2HjVNje9XXiZOArLXGJLKwhfUpr" + + "YwpSIfX6DNWhHUKcwYNca2dkzO10NXPzY7SLgDP9yYyyIV42S/QCCGZ7ku7en/Ve" + + "R/x3CIAOchoBSt6ZRh7iGwl0s5zwVTPF3is4W+46vUECkmbi8HYRjSCEvdAqo7B9" + + "74vNb8tCrwmhztQ3+IdImukSF5/QXMCuaUKLz9CNBZ8pUl45HruG/YxNeJXIC9MV" + + "G1pNZ6D4pBwychwLoM16qjH8/dxZnaIvK8ru09cFNYTowBUOma8u9dpQsMD4+U8O" + + "tQIDAQAB"; + return getPubKey(pubKey); + } + + private static PublicKey getPubKey(String pubKey) { + PublicKey publicKey = null; + try { + java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec( + Base64.getDecoder().decode(pubKey)); + // RSA对称加密算法 + java.security.KeyFactory keyFactory; + keyFactory = java.security.KeyFactory.getInstance(RSA); + // 取公钥匙对象 + publicKey = keyFactory.generatePublic(bobPubKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + e.printStackTrace(); + } + return publicKey; + } + + private static byte[] encrypt(String text, PublicKey pubRSA) throws Exception{ + Cipher cipher = Cipher.getInstance(RSA); + cipher.init(Cipher.ENCRYPT_MODE, pubRSA); + return cipher.doFinal(text.getBytes()); + } + + + // 加密字符串 + public static String encrypt(String text) { + //使用默认公钥 + PublicKey publicKey=getPubKey(); + try { + return byte2hex(encrypt(text, publicKey)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String encrypt(String text,String pubKey) { + //先将公钥生成出来 + PublicKey publicKey=getPubKey(pubKey); + try { + return byte2hex(encrypt(text, publicKey)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String byte2hex(byte[] b) { + String hs = ""; + String stmp = ""; + for (int n = 0; n < b.length; n++) { + stmp = Integer.toHexString(b[n] & 0xFF); + if (stmp.length() == 1) + hs += ("0" + stmp); + else + hs += stmp; + } + return hs.toUpperCase(); + } + + public static byte[] hex2byte(byte[] b) { + if ((b.length % 2) != 0) + throw new IllegalArgumentException("长度不是偶数"); + + byte[] b2 = new byte[b.length / 2]; + + for (int n = 0; n < b.length; n += 2) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } + +// public static void generateKey() throws Exception { +// KeyPairGenerator gen = KeyPairGenerator.getInstance(RSA); +// gen.initialize(512, new SecureRandom()); +// KeyPair keyPair = gen.generateKeyPair(); +// uk = keyPair.getPublic(); +// rk = keyPair.getPrivate(); +// } + +// public final static String encrypt(String text) { +// try { +// return byte2hex(encrypt(text, uk)); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return null; +// } + +// public final static String decrypt(String data) { +// try { +// return new String(decrypt(hex2byte(data.getBytes()))); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return null; +// } + +// private static byte[] decrypt(byte[] src) throws Exception { +// Cipher cipher = Cipher.getInstance(RSA); +// cipher.init(Cipher.DECRYPT_MODE, rk); +// return cipher.doFinal(src); +// } + +// //just for test +// public static void main(String args[]) { +// try { +// RSACrypto.generateKey(); +// String cipherText = RSACrypto.encrypt("cryptology"); +// System.out.println("密文是:" + cipherText); +// String plainText = RSACrypto.decrypt(cipherText); +// System.out.println("明文是:" + plainText); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +} + diff --git a/src/main/java/cn/minoa/util/RSACryptoByQJH.java b/src/main/java/cn/minoa/util/RSACryptoByQJH.java new file mode 100644 index 0000000..a47e244 --- /dev/null +++ b/src/main/java/cn/minoa/util/RSACryptoByQJH.java @@ -0,0 +1,262 @@ +package cn.minoa.util; + +import java.io.*; +import java.security.*; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +import org.apache.commons.codec.binary.Base64; +//import org.apache.commons.io.IOUtils; +//import org.apache.commons.*; + +import java.io.ByteArrayOutputStream; +import java.security.spec.X509EncodedKeySpec; +//import java.util.*; +import javax.crypto.Cipher; +import java.io.FileReader; + +// RSA算法实现 +// 原博文:https://blog.csdn.net/super_cui/article/details/70821268 +// 字符串转公钥:https://blog.csdn.net/petib_wangwei/article/details/37601081 + +public class RSACryptoByQJH{ + private final static String RSA = "RSA"; + public static PublicKey uk; + // public static PrivateKey rk=get; + + /** */ + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 245; + + /** */ + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 128; + // 忽略首行、末行 + private static String getKey(String filename) throws IOException { + String strKeyPEM = ""; + BufferedReader br = new BufferedReader(new FileReader(filename)); + String line=br.readLine(); + String nextline=""; + while ((line = br.readLine()) != null){ + strKeyPEM+=nextline; + nextline=br.readLine(); + if(nextline!=null){ + strKeyPEM += line ; + } + } + br.close(); + return strKeyPEM; + } + + public static String encrypt2(String data) { + + String filepath = "./ndn/mis_rsa_public.pem"; + File file = new File(filepath); + + + RSAPublicKey publicKey = null; + try { + publicKey = getPublicKey(file.getAbsolutePath()); + } catch (IOException e) { + e.printStackTrace(); + } catch (GeneralSecurityException e) { + e.printStackTrace(); + } + // 对数据加密 + try { + Cipher cipher = Cipher.getInstance(RSA); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), publicKey.getModulus().bitLength())); +// int inputLen = data.getBytes().length; + } catch (Exception e) { + throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e); + } + } + + private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){ + int maxBlock = 0; + if(opmode == Cipher.DECRYPT_MODE){ + maxBlock = keySize / 8; + }else{ + maxBlock = keySize / 8 - 11; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] buff; + int i = 0; + try{ + while(datas.length > offSet){ + if(datas.length-offSet > maxBlock){ + buff = cipher.doFinal(datas, offSet, maxBlock); + }else{ + buff = cipher.doFinal(datas, offSet, datas.length-offSet); + } + out.write(buff, 0, buff.length); + i++; + offSet = i * maxBlock; + } + }catch(Exception e){ + throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e); + } + byte[] resultDatas = out.toByteArray(); + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + // IOUtils.closeQuietly(out); + return resultDatas; + } + + + public static RSAPublicKey getPublicKey(String filename) throws IOException, GeneralSecurityException { + String publicKeyPEM = getKey(filename); + return getPublicKeyFromString(publicKeyPEM); + } + + public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException { + String publicKeyPEM = key; + System.out.println("size: "+publicKeyPEM.getBytes().length); + byte[] encoded = Base64.decodeBase64(publicKeyPEM); + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded)); +// RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new PKCS8EncodedKeySpec(encoded)); + return pubKey; + } + + +// private static PublicKey getPubKey() { +// String pubKey= "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UIWR510Jlwk6y287DfW" + +// "WpsCxafpVr2QLeqAaR04qOaaaqXuhZbUoaxgS2HjVNje9XXiZOArLXGJLKwhfUpr" + +// "YwpSIfX6DNWhHUKcwYNca2dkzO10NXPzY7SLgDP9yYyyIV42S/QCCGZ7ku7en/Ve" + +// "R/x3CIAOchoBSt6ZRh7iGwl0s5zwVTPF3is4W+46vUECkmbi8HYRjSCEvdAqo7B9" + +// "74vNb8tCrwmhztQ3+IdImukSF5/QXMCuaUKLz9CNBZ8pUl45HruG/YxNeJXIC9MV" + +// "G1pNZ6D4pBwychwLoM16qjH8/dxZnaIvK8ru09cFNYTowBUOma8u9dpQsMD4+U8O" + +// "tQIDAQAB"; +// return getPubKey(pubKey); +// } +// +// private static PublicKey getPubKey(String pubKey) { +// PublicKey publicKey = null; +// try { +// java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec( +// Base64.getDecoder().decode(pubKey)); +// // RSA对称加密算法 +// java.security.KeyFactory keyFactory; +// keyFactory = java.security.KeyFactory.getInstance(RSA); +// // 取公钥匙对象 +// publicKey = keyFactory.generatePublic(bobPubKeySpec); +// } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { +// e.printStackTrace(); +// } +// return publicKey; +// } + + + +// public static String encrypt(String text,String pubKey) { +// //先将公钥生成出来 +// PublicKey publicKey=getPubKey(pubKey); +// try { +// return byte2hex(encrypt(text, publicKey)); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return null; +// } + + public static String byte2hex(byte[] b) { + String hs = ""; + String stmp = ""; + for (int n = 0; n < b.length; n++) { + stmp = Integer.toHexString(b[n] & 0xFF); + if (stmp.length() == 1) + hs += ("0" + stmp); + else + hs += stmp; + } + return hs.toUpperCase(); + } + + public static byte[] hex2byte(byte[] b) { + if ((b.length % 2) != 0) + throw new IllegalArgumentException("长度不是偶数"); + + byte[] b2 = new byte[b.length / 2]; + + for (int n = 0; n < b.length; n += 2) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } + +// public static void generateKey() throws Exception { +// KeyPairGenerator gen = KeyPairGenerator.getInstance(RSA); +// gen.initialize(512, new SecureRandom()); +// KeyPair keyPair = gen.generateKeyPair(); +// uk = keyPair.getPublic(); +// rk = keyPair.getPrivate(); +// } + +// public final static String encrypt2(String text) { +// try { +// return byte2hex(encrypt(text, uk)); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return null; +// } + +// public final static String decrypt(String data) { +// try { +//// System.out.println("the data is "+data); +//// System.out.println("the data byte is"+data.getBytes()); +//// // System.out.println("the data byte is-"+decrypt(data)); +//// System.out.println("the data byte is-"+Base64.getDecoder().decode(data.getBytes())); +//// return new String(Base64.getDecoder().decode(decrypt(data.getBytes()))); +// Cipher cipher = Cipher.getInstance("RSA"); +// cipher.init(Cipher.DECRYPT_MODE, rk); +// return new String(cipher.doFinal(org.apache.commons.codec.binary.Base64.decodeBase64(data)), "UTF-8"); +// +// // return new String(Base64.getDecoder().decode(decrypt(hex2byte(data.getBytes())))); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return null; +// } + +// private static byte[] decrypt(byte[] src) throws Exception { +// Cipher cipher = Cipher.getInstance(RSA); +// cipher.init(Cipher.DECRYPT_MODE, rk); +// return cipher.doFinal(src); +// } + + + + + + //just for test +// public static void main(String args[]) { +// try { +// // RSACrypto.generateKey(); +//// String filepath="D:\\wefree\\MIN-VPN\\src\\main\\java\\cn\\minoa\\view\\login\\mis_rsa_pubic.pem";//注意filepath的内容; +//// String filepath="./ndn/mis_rsa_public.pem"; +//// File file=new File(filepath); +//// uk=getPublicKey(file.getAbsolutePath()); +//// uk=getPublicKey("D:\\wefree\\MIN-VPN\\src\\main\\java\\cn\\minoa\\view\\login\\mis_rsa_pubic.pem"); +// String cipherText = RSACrypto.encrypt2("cryptology"); +// System.out.println("密文是:" + cipherText); +// // String plainText = RSACrypto.decrypt(cipherText); +// // System.out.println("明文是:" + plainText); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +} +// diff --git a/src/main/java/cn/minoa/util/RSACryptoTest.java b/src/main/java/cn/minoa/util/RSACryptoTest.java new file mode 100644 index 0000000..e6f6895 --- /dev/null +++ b/src/main/java/cn/minoa/util/RSACryptoTest.java @@ -0,0 +1,184 @@ +package cn.minoa.util; + +//import sun.misc.BASE64Decoder; +//import sun.misc.BASE64Encoder; +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * create: https://www.jianshu.com/p/87d2148a4298 + * https://www.cnblogs.com/moonsoft/p/12300001.html + **/ +public class RSACryptoTest { + + /** + * 算法名称 + */ + private static final String ALGORITHM = "RSA"; + + /** + * 密钥长度 + */ + private static final int KEY_SIZE = 2048; + + /** + * 随机生成密钥对(包含公钥和私钥) + */ + public static KeyPair generateKeyPair() throws Exception { + // 获取指定算法的密钥对生成器 + KeyPairGenerator gen = KeyPairGenerator.getInstance(ALGORITHM); + // 初始化密钥对生成器(指定密钥长度, 使用默认的安全随机数源) + gen.initialize(KEY_SIZE); + // 随机生成一对密钥(包含公钥和私钥) + return gen.generateKeyPair(); + } + + /** + * 将 公钥/私钥 编码后以 Base64 的格式保存到指定文件 + */ + public static void saveKeyForEncodedBase64(Key key, File keyFile) throws IOException { + // 获取密钥编码后的格式 + byte[] encBytes = key.getEncoded(); + // 转换为 Base64 文本 +// String encBase64 = new BASE64Encoder().encode(encBytes); + String encBase64=Base64.encodeBase64String(encBytes); + // 保存到文件 + IOUtils.writeFile(encBase64, keyFile); + } + + /** + * 根据公钥的 Base64 文本创建公钥对象 + */ + public static PublicKey getPublicKey(String pubKeyBase64) throws Exception { + // 把 公钥的Base64文本 转换为已编码的 公钥bytes +// byte[] encPubKey = new BASE64Decoder().decodeBuffer(pubKeyBase64); + byte[] encPubKey=Base64.decodeBase64(pubKeyBase64); + // 创建 已编码的公钥规格 + X509EncodedKeySpec encPubKeySpec = new X509EncodedKeySpec(encPubKey); + // 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象 + return KeyFactory.getInstance(ALGORITHM).generatePublic(encPubKeySpec); + } + + /** + * 根据私钥的 Base64 文本创建私钥对象 + */ + public static PrivateKey getPrivateKey(String priKeyBase64) throws Exception { + // 把 私钥的Base64文本 转换为已编码的 私钥bytes +// byte[] encPriKey = new BASE64Decoder().decodeBuffer(priKeyBase64); + byte[] encPriKey =Base64.decodeBase64(priKeyBase64); + // 创建 已编码的私钥规格 + PKCS8EncodedKeySpec encPriKeySpec = new PKCS8EncodedKeySpec(encPriKey); + // 获取指定算法的密钥工厂, 根据 已编码的私钥规格, 生成私钥对象 + return KeyFactory.getInstance(ALGORITHM).generatePrivate(encPriKeySpec); + } + + /** + * 公钥加密数据 + */ + public static byte[] encrypt(byte[] plainData, PublicKey pubKey) throws Exception { + // 获取指定算法的密码器 + Cipher cipher = Cipher.getInstance(ALGORITHM); + // 初始化密码器(公钥加密模型) + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + // 加密数据, 返回加密后的密文 + return cipher.doFinal(plainData); + } + + /** + * 私钥解密数据 + */ + public static byte[] decrypt(byte[] cipherData, PrivateKey priKey) throws Exception { + // 获取指定算法的密码器 + Cipher cipher = Cipher.getInstance(ALGORITHM); + // 初始化密码器(私钥解密模型) + cipher.init(Cipher.DECRYPT_MODE, priKey); + // 解密数据, 返回解密后的明文 + return cipher.doFinal(cipherData); + } + + /** + * 使用配置文件中的公钥进行加密, 返回加密后的数据 + */ + private static byte[] encryptRSAByFile(byte[] plainData, File pubFile) throws Exception { + // 读取公钥文件, 创建公钥对象 + PublicKey pubKey = getPublicKey(IOUtils.readFile(pubFile)); + // 用公钥加密数据 + byte[] cipher = encrypt(plainData, pubKey); + return cipher; + } + + public static byte[] encryptRSA(byte[] plainData) throws Exception { + File pubkeyFile=new File("./ndn/pub.txt"); + return encryptRSAByFile(plainData,pubkeyFile); + } + +// public static byte[] encryptRSA(String plainData) throws Exception { +// return encryptRSA(plainData.getBytes(StandardCharsets.UTF_8)); +// } + + public static String encryptRSAString(String plainData) throws Exception { + byte[] r=encryptRSA(plainData.getBytes(StandardCharsets.UTF_8)); +// return new String(r,StandardCharsets.UTF_8); + String bs=Base64.encodeBase64String(r); + return bs; + } + + // 测试****************************************************************** + public static void main(String[] args) throws Exception { +// // 随机生成一对密钥(包含公钥和私钥) +// KeyPair keyPair = generateKeyPair(); +// // 获取 公钥 和 私钥 +// PublicKey pubKey = keyPair.getPublic(); +// PrivateKey priKey = keyPair.getPrivate(); +// +// // 保存 公钥 和 私钥 +// saveKeyForEncodedBase64(pubKey, new File("./ndn/pub.txt")); +// saveKeyForEncodedBase64(priKey, new File("./ndn/pri.txt")); + + + // 原文数据 +// String data = "Hello, World!"; + String data="{\"password\":\"c4ca4238a0b923820dcc509a6f75849b\",\"aesKey\":[35,-82,-128,-99,-38,-54,-7,106,-16,-3,120,-19,4,-74,-94,101],\"command\":\"/login\",\"username\":\"MYGOD\"}"; + + // 客户端: 加密 + byte[] cipherData = clientEncrypt(data.getBytes(StandardCharsets.UTF_8), new File("./ndn/pub.txt")); +// String bs=Base64.encodeBase64String(cipherData); + String bs=new String(cipherData); + + // 服务端: 解密 + byte[] plainData = serverDecrypt(bs.getBytes(), new File("./ndn/pri.txt")); + + + System.out.println(new String(plainData)); + } + + /** + * 客户端加密, 返回加密后的数据 + */ + private static byte[] clientEncrypt(byte[] plainData, File pubFile) throws Exception { + // 读取公钥文件, 创建公钥对象 + PublicKey pubKey = getPublicKey(IOUtils.readFile(pubFile)); + // 用公钥加密数据 + byte[] cipher = encrypt(plainData, pubKey); + return cipher; + } + + /** + * 服务端解密, 返回解密后的数据 + */ + private static byte[] serverDecrypt(byte[] cipherData, File priFile) throws Exception { + // 读取私钥文件, 创建私钥对象 + PrivateKey priKey = getPrivateKey(IOUtils.readFile(priFile)); + // 用私钥解密数据 + byte[] plainData = decrypt(cipherData, priKey); + return plainData; + } + +} diff --git a/src/main/java/cn/minoa/util/RegisterUtil.java b/src/main/java/cn/minoa/util/RegisterUtil.java new file mode 100644 index 0000000..1a3477b --- /dev/null +++ b/src/main/java/cn/minoa/util/RegisterUtil.java @@ -0,0 +1,116 @@ +package cn.minoa.util; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +public class RegisterUtil { + // 用户记录:通过了区块链注册,但是没有通过后台注册的用户前缀 + + public static boolean isAccessedBlockUser(String prefix) { + // 加密 + String hashString=EncryptionUtil.encryptBasedAES(prefix); + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream("." + File.separator + "ndn" + File.separator + "BlockSuccessUsers.properties")); + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + String filename=settingProperties.getProperty(hashString); + System.out.println(hashString+" : "+filename); + if(filename==null){ + return false; + }else{ + System.out.println("本地存在该文件的断点信息:"+hashString); + return true; + } + } catch (IOException e) { +// e.printStackTrace(); + return false; + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void setAccessedBlockUser(String prefix){ + // 加密 + String hashString=EncryptionUtil.encryptBasedAES(prefix); + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream("."+ File.separator+"ndn"+File.separator+"BlockSuccessUsers.properties")); + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty(hashString, hashString); + outputStream=new BufferedOutputStream(new FileOutputStream("."+ File.separator+"ndn"+File.separator+"BlockSuccessUsers.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"commit changes: add a prefix"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void deleteAccessedBlockUser(String prefix){ + // 加密 + String hashString=EncryptionUtil.encryptBasedAES(prefix); + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream("."+ File.separator+"ndn"+File.separator+"BlockSuccessUsers.properties")); + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.remove(hashString); + outputStream=new BufferedOutputStream(new FileOutputStream("."+ File.separator+"ndn"+File.separator+"BlockSuccessUsers.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"commit changes: delete a prefix"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cn/minoa/util/Rsa.java b/src/main/java/cn/minoa/util/Rsa.java new file mode 100644 index 0000000..114a750 --- /dev/null +++ b/src/main/java/cn/minoa/util/Rsa.java @@ -0,0 +1,100 @@ +package cn.minoa.util; + +import cn.minoa.util.Aes; +import net.named_data.jndn.Face; +import net.named_data.jndn.Interest; +import net.named_data.jndn.Name; +import net.named_data.jndn.util.Blob; + +import javax.crypto.Cipher; +import java.nio.ByteBuffer; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class Rsa { + private static String mPubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UIWR510Jlwk6y287DfW\n" + + "WpsCxafpVr2QLeqAaR04qOaaaqXuhZbUoaxgS2HjVNje9XXiZOArLXGJLKwhfUpr\n" + + "YwpSIfX6DNWhHUKcwYNca2dkzO10NXPzY7SLgDP9yYyyIV42S/QCCGZ7ku7en/Ve\n" + + "R/x3CIAOchoBSt6ZRh7iGwl0s5zwVTPF3is4W+46vUECkmbi8HYRjSCEvdAqo7B9\n" + + "74vNb8tCrwmhztQ3+IdImukSF5/QXMCuaUKLz9CNBZ8pUl45HruG/YxNeJXIC9MV\n" + + "G1pNZ6D4pBwychwLoM16qjH8/dxZnaIvK8ru09cFNYTowBUOma8u9dpQsMD4+U8O\n" + + "tQIDAQAB" ; + + + public static String encrypt(String str) throws Exception{ + byte[] pubKeyBuf = Base64.getMimeDecoder().decode(mPubKey); + RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA"). + generatePublic(new X509EncodedKeySpec(pubKeyBuf)); + Cipher cipher = Cipher.getInstance("RSA") ; + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + byte[] plainText = str.getBytes("UTF-8"); + int encodeBufLen = (plainText.length/245)*256 ; + if(str.length() % 245 != 0) encodeBufLen += 256 ; + byte[] encodeBuf = new byte[encodeBufLen] ; + int encodeIdx = 0 ; + for (int i = 0; i < plainText.length; i+=245) { + int srcBlockLen = 245 ; + if(plainText.length - i < 245) srcBlockLen = plainText.length - i; + byte[] srcBlock = new byte[srcBlockLen] ; + System.arraycopy(plainText,i,srcBlock,0, srcBlockLen); + byte[] dstBlock = cipher.doFinal(srcBlock); + System.arraycopy(dstBlock,0,encodeBuf,encodeIdx,dstBlock.length); + encodeIdx += dstBlock.length; + } + return Base64.getMimeEncoder().encodeToString(encodeBuf) ; + } + + + + public static void main(String[] args){ + + Rsa mRsa = new Rsa(); + Aes aes = new Aes() ; + try { + +// String enString = mRsa.encrypt("寻寻觅觅,冷冷清清,凄凄惨惨戚戚。乍暖还寒时候,最难将息。三杯两盏淡酒,怎敌他、晚来风急!雁过也,正伤心,却是旧时相识。\n" + +// "\n" + +// "满地黄花堆积,憔悴损,如今有谁堪摘?守着窗儿,独自怎生得黑!梧桐更兼细雨,到黄昏、点点滴滴。这次第,怎一个愁字了得!"); + String enString = mRsa.encrypt("{\"password\":\"c4ca4238a0b923820dcc509a6f75849b\",\"aesKey\":\"1234567890abcdef\",\"command\":\"/login\",\"username\":\"MYGOD\"}") ; + System.out.print("en: "+enString); + Face face = new Face("121.15.171.89") ; +// Consumer1 consumer1 = new Consumer1(); + Interest interest = new Interest(new Name("/ndn/edu/pkusz/OA/aesTest")) ; + interest.setMustBeFresh(true) ; + + Base64.Decoder base64Decoder = Base64.getMimeDecoder() ; + interest.setApplicationParameters(new Blob(base64Decoder.decode(enString))) ; + System.out.println("Express interest : " + interest.getName().toUri()); + System.out.println("parameter size = " + interest.getApplicationParameters().size()); + + System.out.println(interest.getName().toUri()); + face.expressInterest(interest, (interest1, data) -> { + System.out.println("onData : " + data.getName()); + Base64.Encoder base64Encoder = Base64.getMimeEncoder(); + + byte[] dataBuf = new byte[data.getContent().size()] ; + data.getContent().buf().get(dataBuf,0,data.getContent().size()) ; + String dataContentBase64 = base64Encoder.encodeToString(dataBuf); + String plainText = null ; + try { + plainText = aes.Decrypt(dataContentBase64,"1234567890abcdef") ; + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("data =========== : " + plainText); + }); + while (true) { + face.processEvents(); + Thread.sleep(1000); + //System.out.println("========================================="); + } + }catch (Exception e){ + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/cn/minoa/util/SecretKeyManageUtil.java b/src/main/java/cn/minoa/util/SecretKeyManageUtil.java new file mode 100644 index 0000000..6f55d37 --- /dev/null +++ b/src/main/java/cn/minoa/util/SecretKeyManageUtil.java @@ -0,0 +1,306 @@ +package cn.minoa.util; + +import cn.minoa.dataRequestInterface.KeyManager; +import cn.minoa.dataRequestInterface.MinoaDataAPI; +import net.named_data.jndn.Name; +import net.named_data.jndn.encoding.EncodingException; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SafeBag; +import net.named_data.jndn.security.pib.Pib; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.security.tpm.Tpm; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.v2.CertificateV2; +import net.named_data.jndn.util.Blob; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class SecretKeyManageUtil { + + // 导出密钥 + public static boolean exportSafeBag(String filepathname) { + try { + // 得到指定identity的证书(包含秘钥信息) + CertificateV2 certificateV2 = KeyManager.INSTANCE + .getCertification(MinoaDataAPI.keyChainNameString); + + // 通过KeyChain导出SafeBag + // exportSafeBag支持设置密码 + SafeBag safeBag = KeyManager.INSTANCE + .getKeyChain().exportSafeBag(certificateV2); + + // 将safeBag进行编码 + Blob blob = safeBag.wireEncode(); + + // 接着就可以将编码后的数据写入到一个文件当中 + writeByteToFile(blob.getImmutableArray(), blob.size(),filepathname); + return true; + }catch (PibImpl.Error | Pib.Error | KeyChain.Error e){ + e.printStackTrace(); + System.out.println(e.getMessage()); + return false; + } + } + + // 导入密钥 + public static String importSafeBag(String filepathname) { + try { + System.out.println("密钥文件:"+filepathname); + // 读取文件,得到字节数组, +// ByteBuffer bb = readByteBuffFromFile(filepathname); + byte[] b=readByteFromFile(filepathname); + + if (b == null) { + return "The read file is empty."; + } + Blob blob=new Blob(b); + // 使用SafeBag的其中一种构造方法 + SafeBag safeBag = new SafeBag(blob); + + String name=safeBag.getCertificate().getName().toString(); + System.out.println("导入文件的证书名称:"+name); +// int flag=0,i=0; +// for(i=0;i 0) { + // do nothing + // System.out.println("reading"); + } + return byteBuffer.array(); + } catch (IOException e) { + e.printStackTrace(); + throw e; + } finally { + try { + channel.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + fs.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + // 读取文件数据到一个bytebuff + public static ByteBuffer readByteBuffFromFile() throws IOException { + return readByteBuffFromFile("./ndn/SecretKey.txt"); + } + /** + * NIO way + * + * @param + * @return + * @throws IOException + */ + public static ByteBuffer readByteBuffFromFile(String filepathname) throws IOException { + File f = new File(filepathname); + if (!f.exists()) { + return null; + } + + FileChannel channel = null; + FileInputStream fs = null; + try { + fs = new FileInputStream(f); + channel = fs.getChannel(); + ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size()); + while ((channel.read(byteBuffer)) > 0) { + // do nothing + // System.out.println("reading"); + } + return byteBuffer; + } catch (IOException e) { + e.printStackTrace(); + throw e; + } finally { + try { + channel.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + fs.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + // 写入byte数组到文件 + private static void writeByteToFile(byte[] content, int len) { + writeByteToFile(content,len,"./ndn/SecretKeyMIN"); + } + + // 写入byte数组到文件 + private static void writeByteToFile(byte[] content, int len,String filepathname) { + try { + File file = new File(filepathname); + FileOutputStream fos = null; + if (file.exists()) { + file.delete(); + } + file.createNewFile();//如果文件不存在,就创建该文件 + fos = new FileOutputStream(file);//首次写入获取 + fos.write(content, 0, len); + //写入完成关闭流 + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 写入字符串到文件 + private static void writeStringToFile(String content) { + try { + File file = new File("./ndn/SecretKey.txt"); + FileOutputStream fos = null; + if (file.exists()) { + file.delete(); + } + file.createNewFile();//如果文件不存在,就创建该文件 + fos = new FileOutputStream(file);//首次写入获取 + OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");//指定以UTF-8格式写入文件 + osw.write(content); + //写入完成关闭流 + osw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/cn/minoa/util/StringByteLengthUtil.java b/src/main/java/cn/minoa/util/StringByteLengthUtil.java new file mode 100644 index 0000000..c364f40 --- /dev/null +++ b/src/main/java/cn/minoa/util/StringByteLengthUtil.java @@ -0,0 +1,37 @@ +package cn.minoa.util; + +import java.io.UnsupportedEncodingException; + +public class StringByteLengthUtil { + + //获取字符串的字节长度 + public static int getByteLength(String s) { +// int length = 0; +// for (int i = 0; i < s.length(); i++) { +// int ascii = Character.codePointAt(s, i); +// if (ascii >= 0 && ascii <= 255) { +// length++; +// } else { +// length += 3; +// } +// } +// return length; + try { + return s.getBytes("UTF-8").length; + } catch (UnsupportedEncodingException e) { +// e.printStackTrace(); + System.out.println("UTF-8不能被getBytes()函数识别..."); + return s.getBytes().length; + } + } + + //判断字符串是否全是数字 + public static boolean isAllNumber(String s){ + for(int i=0;i57))){ + return false; + } + } + return true; + } +} diff --git a/src/main/java/cn/minoa/util/Tool.java b/src/main/java/cn/minoa/util/Tool.java new file mode 100644 index 0000000..3018e38 --- /dev/null +++ b/src/main/java/cn/minoa/util/Tool.java @@ -0,0 +1,85 @@ +package cn.minoa.util; +// 根据字符串长度计算pane的高和宽 + +public class Tool { + public static double getWidth(String Msg){ + int len = Msg.length(); + double width=20; + for(int i=0;i=330){ +// height+=17.4; + height+=18; + width=20; + } + } + return height; + } + + private static final boolean isChinese(char c) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { + return true; + } + return false; + } + + private static boolean isDigit(char ch) { + return Character.isDigit(ch); + } + + private static boolean isBigLetter(char ch){ + if((ch>=65)&&(ch<=90)){ + return true; + }else{ + return false; + } + } + + private static boolean isLittleLetter(char ch){ + if((ch>=97)&&(ch<=122)){ + return true; + }else{ + return false; + } + } +} diff --git a/src/main/java/cn/minoa/util/testFileMD5.java b/src/main/java/cn/minoa/util/testFileMD5.java new file mode 100644 index 0000000..8fb2a48 --- /dev/null +++ b/src/main/java/cn/minoa/util/testFileMD5.java @@ -0,0 +1,25 @@ +package cn.minoa.util; + +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.FileInputStream; +import java.io.IOException; + +public class testFileMD5 { + + public static void main(String[] args){ + Long startTime = System.currentTimeMillis(); + // File +// String path="E:\\系统镜像\\ubuntu-18.04.3-desktop-amd64.iso"; + String path="E:\\系统镜像\\UltraISO.9.7.0.3476.exe"; + try { + String res= DigestUtils.md5Hex(new FileInputStream(path)); + System.out.println("文件加密结果:"+res); + } catch (IOException e) { + e.printStackTrace(); + } + Long endTime=System.currentTimeMillis(); + System.out.println("文件加密耗时:"+(endTime-startTime)+"ms"); + } + +} diff --git a/src/main/java/cn/minoa/view/NavigationBarController.java b/src/main/java/cn/minoa/view/NavigationBarController.java new file mode 100644 index 0000000..09c27ea --- /dev/null +++ b/src/main/java/cn/minoa/view/NavigationBarController.java @@ -0,0 +1,124 @@ +package cn.minoa.view; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +import cn.minoa.MainApp; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.shape.Line; +import javafx.scene.text.Text; + +public class NavigationBarController implements Initializable{ + private MainApp mainApp; + + @FXML + private ImageView logoPhoto; + @FXML + private ImageView headPhoto; + @FXML + private Text username; + + @FXML + private Line myFileLine; + @FXML + private Line fileShareLine; + + @FXML + private AnchorPane mainBar; + + @FXML + private AnchorPane secondaryBar; + + @FXML + private AnchorPane mainContext; + + public NavigationBarController() { + // TODO Auto-generated constructor stub + } + + @FXML + private void initialize() { + //在fxml被载入时候被自动调用 + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + showHeadPhoto(); + myFileLine.setVisible(true); + fileShareLine.setVisible(false); + } + + @FXML + private void handleHeadPhotoShow() { + mainApp.showHeadPhotoOverview(); + } + + @FXML + private void showMyFileView() throws IOException { + // 打开我的文件 + mainApp.showMineListOverview(); + myFileLine.setVisible(true); + fileShareLine.setVisible(false); + } + + @FXML + private void showFileShareView() throws IOException { + //打开文件共享 + mainApp.showMessageListOverview(); + myFileLine.setVisible(false); + fileShareLine.setVisible(true); + } + + //界面跳转=>将第二层级目录内容替换 + public void skipListView(FXMLLoader loader) { + //清空子节点 + ObservableList secondaryBarChildren=secondaryBar.getChildren(); + secondaryBarChildren.clear(); + + try { + AnchorPane listOverview=(AnchorPane)loader.load(); + secondaryBarChildren.add(listOverview); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + //界面跳转=>将主内容替换 + public void skipMainContextView(FXMLLoader loader) { + //清空子节点 + ObservableList mainContextChildren=mainContext.getChildren(); + mainContextChildren.clear(); + + try { + AnchorPane mainOverview=(AnchorPane)loader.load(); + mainContextChildren.add(mainOverview); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + // TODO Auto-generated method stub + } + + //设置头像 + private void showHeadPhoto() { + this.logoPhoto.setImage(new Image("file:resources/images/logo_mini.png")); + this.headPhoto.setImage(new Image("file:resources/images/headPhotos/"+mainApp.loginer.getUserName()+".png")); + username.setText(mainApp.loginer.getUserName()); + } +} diff --git a/src/main/java/cn/minoa/view/cloudfile/FileShareController.java b/src/main/java/cn/minoa/view/cloudfile/FileShareController.java new file mode 100644 index 0000000..74657e3 --- /dev/null +++ b/src/main/java/cn/minoa/view/cloudfile/FileShareController.java @@ -0,0 +1,711 @@ +package cn.minoa.view.cloudfile; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.LinkedList; +/* + * 数据格式说明: + * f 普通文件 + * o 其它文件 + * d 文件夹目录 + * .前端一个listview表项是一个pane,其id是"路径+纯文件名+@文件类型" + */ +import java.util.List; +import java.util.Map; + +import cn.minoa.model.SingleFile; +import cn.minoa.util.FileUtil; +import cn.minoa.util.StringByteLengthUtil; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; +import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +// 说明:下载文件时传入的filename是: "文件所属共享者的用户名"+"@"+"文件名",如: wefree@a.txt +// 取消共享传入的filename是: "纯文件名",如: a.txt +// 设置共享传入的filename是: "文件所在服务器端路径"+"文件名",如: /cache/a.txt +// ls共享目录传入的filename为冗余信息,这里客户端设定为了"." + +public class FileShareController { + private MainApp mainApp; + // 当前路径下的文件列表[path+file+@type] + private List filePathnameList; + // 记录当前选中的文件: shareusername@filename + private String currentfilename; + // 当前文件列表对应的文件具体信息,key是filePathnameList中的表项 + private Map detailedFileListMap; + + @FXML + private ListView fileList; + @FXML + private Button downloadTheFileButton; + @FXML + private Button cancelShareButton; + + private FileListItem fileListItem; + + // 构造函数 + public FileShareController() { + // TODO Auto-generated constructor stub + filePathnameList = new LinkedList(); + detailedFileListMap=new HashMap<>(); + fileListItem=new FileListItem("user@test.txt@t"); + currentfilename="."; + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 默认下载按钮不可用 + downloadTheFileButton.setDisable(true); + cancelShareButton.setDisable(true); + // 监听选择的改变 + fileList.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showButton(newValue.getId())); + } + + // 点击事件触发:生效下载按钮、取消共享按钮 + private void showButton(String paneId) { + // 判断是否是文件夹,是,则进入下一个界面 + if (checkFolder(paneId)) { + //这种情况在这里不会发生 + } else if (checkFile(paneId)) { + currentfilename=fileListItem.getOwnerNameFromPaneId(paneId)+"@"+fileListItem.getPureFilenameFromPaneId(paneId); + // 修改按钮属性 + downloadTheFileButton.setDisable(false); + if(fileListItem.getOwnerNameFromPaneId(paneId).equals(mainApp.loginer.getUserName())){ + // 如果选中文件的所有者是系统登录者 + cancelShareButton.setDisable(false); + }else { + cancelShareButton.setDisable(true); + } + } + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + // 发送命令请求主目录下的文件夹列表,赋值给filePathnameList + lsShareFiles(); + // 更新显示到前端 + for (int i = 0; i < filePathnameList.size(); i++) { + fileList.getItems().add(new FileListItem(filePathnameList.get(i)).getPaneItem()); + } + } + + // 重新加载当前列表 + private void reloadList(){ + try { + mainApp.showMessageListOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 下载选中项按钮 + @FXML + private void downloadSelectedItem() { + //判断是否选择了文件 + if(fileList.getSelectionModel().getSelectedItem()==null){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件未选择"); + alert.setHeaderText("文件未选择"); + alert.setContentText("请先在文件列表中选择一个文件。"); + alert.showAndWait(); + return; + } + // 选择要下载到的文件夹 + String localDirString = mainApp.fileLocalPath; + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("选择要下载到的本地文件夹"); + Stage selectFileStage = new Stage(); + File file = directoryChooser.showDialog(selectFileStage); + if (file != null) { + // 文件不为空,且是一个文件夹 + if (file.isDirectory()) { + localDirString = file.getPath() + "\\"; + System.out.println("选中的本地保存文件夹为:" + localDirString); + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("选中类型错误"); + alert.setHeaderText("你的选中类型错误"); + alert.setContentText("请确保你选中的是一个文件夹而不是一个文件。"); + alert.showAndWait(); + return; + } + }else{ + return ; + } + + String sharefilename=currentfilename; + if(currentfilename.equals(".")){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("尚未选中文件"); + alert.setHeaderText("尚未选中文件"); + alert.setContentText("请先选中一个文件。"); + alert.showAndWait(); + return; + } + System.out.println("开始下载... :" + sharefilename); + mainApp.downloadFileByPathfilenameWithProgressBar(sharefilename,localDirString); + // 判断是否手动取消了下载,是的话就不进行下面的判断 + if(mainApp.isCancelFileTransferFlag){ + return; + } + // 下载文件之后,通过获取服务器的文件大小,与下载到本地的文件大小对比 + // 判断是否传输成功 + Long fileServerLength=mainApp.getServerFileByteLength(sharefilename); + System.out.println("fileServerLength: "+fileServerLength); + String localfilename=localDirString+getPureFileNameFromLinux(sharefilename); + File localfile=new File(localfilename); + if(!localfile.exists()){ + // 本地文件未被成功创建 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("本地文件未被成功创建,请检查是否有权限访问你选择的文件存储位置。" + "您的指定的文件保存位置为:" +localDirString); + alert.showAndWait(); + }else if(localfile.length()==fileServerLength){ + // 本地文件成功创建且字节长度与服务器一致 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("下载成功"); + alert.setHeaderText("下载文件成功"); + alert.setContentText("您请求的文件数据已经被全部下载到本地," + "您的文件保存位置为:" + + localDirString); + alert.showAndWait(); + }else{ + // 本地文件成功创建但字节长度与服务器不一致 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("文件数据未能全部写入本地。" + "您的文件保存位置为:" +localDirString+ + "\n 服务器文件大小为:"+fileServerLength+"\n 本地文件大小为:"+localfile.length()); + alert.showAndWait(); + } + } + + // 根据包含路径的文件名获取纯文件名[linux下] + public String getPureFileNameFromLinux(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '/') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[linux]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[linux]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } + + // 文件操作命令 + private String getFileActionJson(String cmd, String filename) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/fileAction"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String cmdString = cmd; + String filenameString = filename; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("fileCmd", cmdString); + jsonObject.put("filename", filenameString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //从currentfilename中取出纯文件名 + private String getPureFilenameFormCurrentfilename(String cf){ + String currentfilenameString=cf; + if(!currentfilenameString.contains("@")){ + return "error"; + } + int index=-1; + for(int i=0;i15){ + thisownername=thisownername.substring(0,5)+" ..."; + } + userText.setText(thisownername); + // 头像和气泡文本内容 + String thisfilename=getPureFilenameFromPaneId(name); + if(StringByteLengthUtil.getByteLength(thisfilename)>20){ + thisfilename=thisfilename.substring(0,10)+" ..."; + } + text.setText(thisfilename); + setHeadPortrait(head, getFileTypeFromPaneId(name)); + // 文件大小 + Long size=detailedFileListMap.get(name).getSize(); + filesizeText.setText(FileUtil.sizeToShowSize(size)); + // 文件创建时间 + Long time=detailedFileListMap.get(name).getTime(); + filetimeText.setText(FileUtil.timeToDateString(time)); + // 文件大小和文件创建时间 + pane.getChildren().add(userText); + pane.getChildren().add(head); + pane.getChildren().add(text); + pane.getChildren().add(filesizeText); + pane.getChildren().add(filetimeText); + // 设置pane的id + pane.setId(name); + return pane; + } + + // 从包含文件所有者、文件类型的文件名获取文件所有者 + public String getOwnerNameFromPaneId(String paneId) { + // 标记第一个@的下标 + int index = -1; + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + index = i; + break; + } + } + if (index==-1) { + System.out.println("prase paneId error: " + paneId); + return ""; + } + return paneId.substring(0, index); + } + // 根据包含文件共享者、文件类型的文件名获取纯文件名 + public String getPureFilenameFromPaneId(String paneId) { + // 标记第一个@的下标 + int indexLeft = -1; + // 标记最后一个@的下标 + int indexRight = -1; + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + indexLeft = i; + break; + } + } + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + indexRight = i; + } + } + if ((indexLeft == -1) || (indexRight == -1)) { + System.out.println("prase paneId error: " + paneId); + return ""; + } + return paneId.substring(indexLeft + 1, indexRight); + } + + // 从paneId中获取文件类型 + public String getFileTypeFromPaneId(String paneId) { + return paneId.substring(paneId.length() - 1, paneId.length()); + } + } +} + diff --git a/src/main/java/cn/minoa/view/home/DealSingleReimbursementController.java b/src/main/java/cn/minoa/view/home/DealSingleReimbursementController.java new file mode 100644 index 0000000..40b7489 --- /dev/null +++ b/src/main/java/cn/minoa/view/home/DealSingleReimbursementController.java @@ -0,0 +1,263 @@ +package cn.minoa.view.home; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.Email; +import cn.minoa.model.Reimbursement; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class DealSingleReimbursementController { + private MainApp mainApp; + private Stage dialogStage; + private Integer aprovalId; + + @FXML + private Text senderText; + @FXML + private Text receiverText; + @FXML + private Text processText; + @FXML + private Text moneyText; + @FXML + private Text numberText; + @FXML + private Text contentText; + @FXML + private TextArea detailTextArea; + + @FXML + private Text reimFilesTitleText; + @FXML + private ListView reimFiles; + + public DealSingleReimbursementController(){ + aprovalId=0; + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + reimFiles.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> downloadReimFile(newValue.getId())); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + public void setReimInfo(Reimbursement reimbursement) { + senderText.setText(reimbursement.getSender()); + receiverText.setText(reimbursement.getReceiver()); + processText.setText(reimbursement.getProcess().toString()); + moneyText.setText(reimbursement.getTotalMoney().toString()); + numberText.setText(reimbursement.getNumber().toString()); + contentText.setText(reimbursement.getContent()); + detailTextArea.setText(reimbursement.getDetail()); + aprovalId=reimbursement.getId(); + // 如果邮件带有附件,将其显示到前端 + showReimFiles(reimbursement); + } + + private void showReimFiles(Reimbursement reimbursement) { + if (reimbursement.getFilesNumber() == 0) { + // 没有文件,隐藏附件列表 + // ... + reimFilesTitleText.setText("报销凭证【当前报销申请无附加凭证】"); + } else { + // 有文件,展示到前端 + for (int i = 0; i < reimbursement.getFilesNumber(); i++) { + reimFiles.getItems().add(new reimbursementFileListItem(reimbursement.files.get(i)).getPaneItem()); + } + } + } + + //从服务器下载选中的附件到指定的本地文件夹下 + private void downloadReimFile(String pathfilename) { + //选择要下载到的文件夹 + String localDirString=mainApp.fileLocalPath; + DirectoryChooser directoryChooser=new DirectoryChooser(); + directoryChooser.setTitle("选择要下载到的本地文件夹"); + Stage selectFileStage = new Stage(); + File file = directoryChooser.showDialog(selectFileStage); + if (file != null) { + //文件不为空,且不是文件夹 + if(file.isDirectory()) { + localDirString=file.getPath()+"\\"; + System.out.println("选中的文件夹为:"+localDirString); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("选中类型错误"); + alert.setHeaderText("你的选中类型错误"); + alert.setContentText("请确保你选中的是一个文件夹而不是一个文件。"); + alert.showAndWait(); + } + }else{ + return ; + } + + System.out.println("开始下载... :"+pathfilename); +// int flag=mainApp.downloadFileByPathfilename(pathfilename,localDirString); + int flag=-1; + if(flag==0) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("下载成功"); + alert.setHeaderText("下载文件成功"); + alert.setContentText("您请求的文件数据已经被全部下载到本地," + + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" + + "您的文件保存位置为:"+localDirString); + alert.showAndWait(); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("本地文件数据写入失败,请检查是否有权限访问默认存储位置。" + + "您的文件默认保存位置为:"+localDirString); + alert.showAndWait(); + } + } + + public void setDialogStage(Stage stage) { + this.dialogStage = stage; + } + + @FXML + private void exit() { + this.dialogStage.close(); + } + + @FXML + private void passReim(){ + //通过报销 + //否决报销 + Integer r=makeReim(1); + if(r==0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("通过报销成功"); + alert.setHeaderText("已通过该报销申请"); + alert.setContentText("已通过该报销申请,报销的ID为:"+aprovalId); + alert.showAndWait(); + //刷新 + try { + mainApp.reloadReimbursementDealOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + //退出 + exit(); + }else{ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("通过报销失败"); + alert.setHeaderText("通过该报销申请失败"); + alert.setContentText("由于未知原因,不能通过该报销申请,报销的ID为:"+aprovalId); + alert.showAndWait(); + exit(); + } + } + + @FXML + private void vetoReim(){ + //否决报销 + Integer r=makeReim(0); + if(r==0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("否决报销成功"); + alert.setHeaderText("已否决该报销申请"); + alert.setContentText("已否决该报销申请,报销的ID为:"+aprovalId); + alert.showAndWait(); + //刷新 + try { + mainApp.reloadReimbursementDealOverview(); + } catch (IOException e) { + e.printStackTrace(); + } + //退出 + exit(); + }else{ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("否决报销失败"); + alert.setHeaderText("否决该报销申请失败"); + alert.setContentText("由于未知原因,不能否决该报销申请,报销的ID为:"+aprovalId); + alert.showAndWait(); + exit(); + } + } + + //审核报销:0不通过,1通过 + private Integer makeReim(Integer rstCode){ + if(aprovalId==0){ + return -1; + } + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getDealApproval(aprovalId,rstCode)); + mainApp.minoaDataAPI.executeOrder("/dealApproval", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + return 0; + } else { + return -1; + } + } + + // 审核报销命令: 0表示不通过,1表示通过 + private String getDealApproval(Integer aprId,Integer rstCode) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/dealApproval"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer approvalIdInteger = aprId; + Integer approvalRstInteger = rstCode; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("approvalId", approvalIdInteger); + jsonObject.put("approvalRst", approvalRstInteger); + jsonObject.put("reason","no reason"); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + +} diff --git a/src/main/java/cn/minoa/view/home/HomeListController.java b/src/main/java/cn/minoa/view/home/HomeListController.java new file mode 100644 index 0000000..7f78454 --- /dev/null +++ b/src/main/java/cn/minoa/view/home/HomeListController.java @@ -0,0 +1,54 @@ +package cn.minoa.view.home; + +import cn.minoa.MainApp; +import javafx.fxml.FXML; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +import java.io.IOException; + +public class HomeListController { + @SuppressWarnings("unused") + private MainApp mainApp; + + @FXML + private ImageView reimbursement; + @FXML + private ImageView entry_examination; + @FXML + private ImageView leave_office; + @FXML + private ImageView evection; + @FXML + private ImageView work_summary; + + public HomeListController() { + } + + @FXML + private void initialize() { + //在fxml被载入时候被自动调用 + reimbursement.setImage(new Image("file:resources/images/reimbursement.png")); + entry_examination.setImage(new Image("file:resources/images/entry_examination.png")); + leave_office.setImage(new Image("file:resources/images/leave_office.png")); + evection.setImage(new Image("file:resources/images/evection.png")); + work_summary.setImage(new Image("file:resources/images/work_summary.png")); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + @FXML + private void showReimApplyView() throws IOException { + mainApp.showReimbursementOverview(); + } + + @FXML + private void showReimDealView() throws IOException { + mainApp.showReimbursementDealOverview(); + } +} diff --git a/src/main/java/cn/minoa/view/home/ReimbursementApplyController.java b/src/main/java/cn/minoa/view/home/ReimbursementApplyController.java new file mode 100644 index 0000000..6abcafe --- /dev/null +++ b/src/main/java/cn/minoa/view/home/ReimbursementApplyController.java @@ -0,0 +1,478 @@ +package cn.minoa.view.home; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +import cn.minoa.model.Reimbursement; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; + +public class ReimbursementApplyController { + private MainApp mainApp; + private List reimbursementInfoList; + private final String CONTROLSTRING_SEND;//表示“我发起的” + private final String CONTROLSTRING_RECEIVE;//表示“我审批的” + + @FXML + private Text listTitleText; + @FXML + private ListView reimbursementList; + + public ReimbursementApplyController() { + // TODO Auto-generated constructor stub + reimbursementInfoList = new LinkedList(); + CONTROLSTRING_RECEIVE = "R"; + CONTROLSTRING_SEND = "S"; + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + reimbursementList.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showReimbursementDetails(Integer.parseInt(newValue.getId()))); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + // 被mainApp调用 + this.mainApp = mainApp; + // 缺省标题 + listTitleText.setText("我申报的报销列表"); + // 初始化list + getReimbursement(0); + //修改状态 + mainApp.currentReimPane=mainApp.MAIN_REIMPANE_SEND; + // 反应到前端 + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursementList.getItems().add(new ReimbursementListItem(reimbursementInfoList.get(i)).getPaneItem()); + } + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp, String controlString) { + // 被mainApp调用 + this.mainApp = mainApp; + if (controlString.equals(CONTROLSTRING_RECEIVE)) { + // 缺省标题 + listTitleText.setText("我审核的报销列表"); + // 初始化list + getReimbursement(1); + //修改状态 + mainApp.currentReimPane=mainApp.MAIN_REIMPANE_REC; + // 反应到前端 + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursementList.getItems().add(new ReimbursementListItem(reimbursementInfoList.get(i)).getPaneItem()); + } + } else { + // 缺省标题 + listTitleText.setText("我申报的报销列表"); + // 初始化list + getReimbursement(0); + //修改状态 + mainApp.currentReimPane=mainApp.MAIN_REIMPANE_SEND; + // 反应到前端 + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursementList.getItems().add(new ReimbursementListItem(reimbursementInfoList.get(i)).getPaneItem()); + } + } + } + + // 切换成我审批的报销审批 + @FXML + private void switchToSendBox() { + try { + mainApp.reloadReimbursementOverview(CONTROLSTRING_SEND); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 切换成我发出的报销审批 + @FXML + private void switchToRecvBox() { + try { + mainApp.reloadReimbursementOverview(CONTROLSTRING_RECEIVE); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 展示报销的详细信息的弹窗 + private void showReimbursementDetails(Integer paneId) { + // 在当前数据结构中找到这个emailId对应的邮件 + Reimbursement reimbursement = new Reimbursement(); + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursement = reimbursementInfoList.get(i); + if (reimbursement.getId().equals(paneId)) { + break; + } + } + // 传递给主函数 + mainApp.showReimbursementDetailsDialog(reimbursement); + } + + // 申请报销弹窗 + @FXML + private void handleWritter() { + mainApp.showWritterReimbursementDialog(); + } + + //刷新列表 + @FXML + private void refreshList(){ + if(mainApp.currentMainPane.equals(mainApp.MAIN_REIMPANE)&&mainApp.currentReimPane.equals(mainApp.MAIN_REIMPANE_SEND)){ + //面板显示的自己申报的报销列表 + try { + mainApp.reloadReimbursementOverview("S"); + } catch (IOException e) { + e.printStackTrace(); + } + }else if(mainApp.currentMainPane.equals(mainApp.MAIN_REIMPANE)&&mainApp.currentReimPane.equals(mainApp.MAIN_REIMPANE_REC)){ + //面板显示的要自己审核的报销列表 + try { + mainApp.reloadReimbursementOverview("R"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // 发送数据请求请求发送给登录者的报销列表 + private Integer getReimbursement(Integer queryCode) { + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetApproval(queryCode)); + mainApp.minoaDataAPI.executeOrder("/getApproval", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if ((dataString.equals("")) || (dataString.equals("null"))) { + reimbursementInfoList.clear(); + return 0; + } + reimbursementInfoList.clear(); + JSONArray dataJsonArray = new JSONArray(dataString); + String reimbursementCacheString; + JSONObject reimbursementCacheJsonObject; + // 遍历每个邮件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + reimbursementCacheString = dataJsonArray.getString(i); + reimbursementCacheJsonObject = new JSONObject(reimbursementCacheString); + Integer id = reimbursementCacheJsonObject.getInt("approvalId"); + String senderInfo = reimbursementCacheJsonObject.getString("proposer"); + // 解析发送者和接收者的姓名 + String receiverInfo = reimbursementCacheJsonObject.getString("approver"); + Integer process=reimbursementCacheJsonObject.getInt("process"); + String detailString = reimbursementCacheJsonObject.getString("detail"); + JSONObject receiverJsonObject = new JSONObject(receiverInfo); + JSONObject senderJsonObject = new JSONObject(senderInfo); + String sender_usernameString = senderJsonObject.getString("username"); + String receiver_usernameString = receiverJsonObject.getString("username"); + // 解析报销详细内容 + System.out.println("报销详细: "+detailString); + detailString=detailString.substring(1,detailString.length()-1); + System.out.println("处理后的报销详细: "+detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + Long detail_date = detailJsonObject.getLong("date"); + String detail_content = detailJsonObject.getString("content"); + Integer detail_invoiceNum = detailJsonObject.getInt("invoiceNum"); + Double detail_totalMoney = detailJsonObject.getDouble("totalMoney"); + String detail_detail = detailJsonObject.getString("detail"); + //解析邮件的附件列表 + String filesString=detailJsonObject.getString("pictures"); + JSONArray filesJsonArray=new JSONArray(filesString); + List reimbursementfiles=new LinkedList(); + for(int j=0;j reimbursementfiles = new LinkedList(); + for (int j = 0; j < filesJsonArray.length(); j++) { + String onefileString = filesJsonArray.getString(j); + reimbursementfiles.add(onefileString); + } + //构建一个报销对象 + Reimbursement reimbursement = new Reimbursement(); + reimbursement.setId(id); + reimbursement.setProcess(process); + reimbursement.setDate(detail_date); + reimbursement.setContent(detail_content); + reimbursement.setDetail(detail_detail); + reimbursement.setNumber(detail_invoiceNum); + reimbursement.setTotalMoney(detail_totalMoney); + reimbursement.setSender(sender_usernameString); + reimbursement.setReceiver(receiver_usernameString); + reimbursement.files = reimbursementfiles; + reimbursementInfoList.add(reimbursement); + } + return 0; + } catch (JSONException e) { + e.printStackTrace(); + return -1; + } + } + return 0; + } else { + return -1; + } + } + + // 获取记录命令: 0表示查询我发送的,1表示查询发送给我的 + private String getGetApproval(Integer queryCode) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getApproval"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer typeCodeInteger = 2; + Integer queryCodeInteger = queryCode; + Integer pageNoInteger = 0; // 页编号 + Integer pageSizeInteger = 100; // 页大小 + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("typeCode", typeCodeInteger); + jsonObject.put("queryCode", queryCodeInteger); + jsonObject.put("pageNo", pageNoInteger); + jsonObject.put("pageSize", pageSizeInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} + +class ReimbursementListItem { + // 外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + // 头像 + private Button head; + private Integer headLength; + // 文本区域 + private Text text; // 显示报销发送者姓名 + private Text titleText; // 显示报销审核状态 + + private Reimbursement reimbursement; + + // 组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + private Integer titleTextXInteger; + private Integer titleTextYInteger; + + // 设置联系人头像 + private void setHeadPortrait(Button button, String username) { + button.setStyle( + String.format("-fx-background-image: url('file:resources/images/headPhotos/%s.png')", username)); + } + + // 构造函数 + public ReimbursementListItem(Reimbursement reimbursement) { + // 数据配置 + this.reimbursement = reimbursement; + // 大小配置 + paneWidth = 200; + paneHeight = 50; + headLength = 35; + // 位置配置 + headXInteger = 10; + headYInteger = 10; + textXInteger = 70; + textYInteger = 28; + titleTextXInteger = 250; + titleTextYInteger = 28; + + pane = new Pane(); + head = new Button(); + text = new Text(); + titleText = new Text(); + + // 最外面的pane的大小 + pane.setPrefSize(paneWidth, paneHeight); + + // 设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + + // 设置消息头像和消息文本属性 + head.setPrefSize(headLength, headLength); + } + + // 根据email获取pane + public Pane getPaneItem() { + // 头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + // 发送人名位置 + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + // 审核状态位置 + titleText.setLayoutX(titleTextXInteger); + titleText.setLayoutY(titleTextYInteger); + // 头像和气泡文本内容 + text.setText(reimbursement.getSender()+"提交的报销申请"); + titleText.setText("【"+getProcessString(reimbursement.getProcess())+"】"); + setHeadPortrait(head, reimbursement.getSender()); + // 赋值子节点 + pane.getChildren().add(head); + pane.getChildren().add(text); + pane.getChildren().add(titleText); + // 设置pane的id + pane.setId(reimbursement.getId().toString()); + return pane; + } + //根据报销状态字节码获取报销状态 + private String getProcessString(Integer type){ + if(type==0){ + return "等待处理"; + }else if(type==1){ + return "已处理"; + }else if(type==2){ + return "审批未通过"; + }else if(type==3){ + return "审批通过"; + }else{ + return "审核进度未知"; + } + } +} diff --git a/src/main/java/cn/minoa/view/home/ReimbursementDealController.java b/src/main/java/cn/minoa/view/home/ReimbursementDealController.java new file mode 100644 index 0000000..db8828f --- /dev/null +++ b/src/main/java/cn/minoa/view/home/ReimbursementDealController.java @@ -0,0 +1,306 @@ +package cn.minoa.view.home; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.Reimbursement; +import javafx.fxml.FXML; +import javafx.scene.control.ListView; +import javafx.scene.layout.Pane; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +public class ReimbursementDealController { + private MainApp mainApp; + private List reimbursementInfoList; + + @FXML + private ListView reimbursementList; + + public ReimbursementDealController() { + reimbursementInfoList = new LinkedList(); + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + reimbursementList.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showReimbursementDetails(Integer.parseInt(newValue.getId()))); + } + + // 展示报销的详细信息的弹窗 + private void showReimbursementDetails(Integer paneId) { + // 在当前数据结构中找到这个emailId对应的邮件 + Reimbursement reimbursement = new Reimbursement(); + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursement = reimbursementInfoList.get(i); + if (reimbursement.getId().equals(paneId)) { + break; + } + } + // 传递给主函数 + mainApp.dealReimbursementDetailsDialog(reimbursement); + } + + //弹出一个可以选择通过审批或者拒绝通过审批的弹窗 + private void dealReimbursementDetails(Integer paneId) { + // 在当前数据结构中找到这个emailId对应的邮件 + Reimbursement reimbursement = new Reimbursement(); + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursement = reimbursementInfoList.get(i); + if (reimbursement.getId() == paneId) { + break; + } + } + //然后发条消息将这个审批给通过或否决 + //... + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + // 被mainApp调用 + this.mainApp = mainApp; + // 初始化list + getReimbursement(1); + // 反应到前端 + for (int i = 0; i < reimbursementInfoList.size(); i++) { + reimbursementList.getItems().add(new ReimbursementListItem(reimbursementInfoList.get(i)).getPaneItem()); + } + } + + // 发送数据请求请求发送给登录者的报销列表 + // 0表示我发送的,1表示发送给我的 + // 要判断是否是已经被审核的,process=0表示还审核 + private Integer getReimbursement(Integer queryCode) { + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetApproval(queryCode)); + mainApp.minoaDataAPI.executeOrder("/getApproval", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if ((dataString.equals("")) || (dataString.equals("null"))) { + reimbursementInfoList.clear(); + return 0; + } + reimbursementInfoList.clear(); + JSONArray dataJsonArray = new JSONArray(dataString); + String reimbursementCacheString; + JSONObject reimbursementCacheJsonObject; + // 遍历每个信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + reimbursementCacheString = dataJsonArray.getString(i); + reimbursementCacheJsonObject = new JSONObject(reimbursementCacheString); + Integer id = reimbursementCacheJsonObject.getInt("approvalId"); + String senderInfo = reimbursementCacheJsonObject.getString("proposer"); + // 解析发送者和接收者的姓名 + String receiverInfo = reimbursementCacheJsonObject.getString("approver"); + Integer process = reimbursementCacheJsonObject.getInt("process"); + String detailString = reimbursementCacheJsonObject.getString("detail"); + JSONObject receiverJsonObject = new JSONObject(receiverInfo); + JSONObject senderJsonObject = new JSONObject(senderInfo); + String sender_usernameString = senderJsonObject.getString("username"); + String receiver_usernameString = receiverJsonObject.getString("username"); + // 解析报销详细内容 + System.out.println("报销详细: " + detailString); + detailString = detailString.substring(1, detailString.length() - 1); + System.out.println("处理后的报销详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + Long detail_date = detailJsonObject.getLong("date"); + String detail_content = detailJsonObject.getString("content"); + Integer detail_invoiceNum = detailJsonObject.getInt("invoiceNum"); + Double detail_totalMoney = detailJsonObject.getDouble("totalMoney"); + String detail_detail = detailJsonObject.getString("detail"); + //解析邮件的附件列表 + String filesString = detailJsonObject.getString("pictures"); + JSONArray filesJsonArray = new JSONArray(filesString); + List reimbursementfiles = new LinkedList(); + for (int j = 0; j < filesJsonArray.length(); j++) { + String onefileString = filesJsonArray.getString(j); + reimbursementfiles.add(onefileString); + } + //构建一个报销对象 + Reimbursement reimbursement = new Reimbursement(); + reimbursement.setId(id); + reimbursement.setProcess(process); + reimbursement.setDate(detail_date); + reimbursement.setContent(detail_content); + reimbursement.setDetail(detail_detail); + reimbursement.setNumber(detail_invoiceNum); + reimbursement.setTotalMoney(detail_totalMoney); + reimbursement.setSender(sender_usernameString); + reimbursement.setReceiver(receiver_usernameString); + reimbursement.files = reimbursementfiles; + if (reimbursement.getProcess() == 0) { + reimbursementInfoList.add(reimbursement); + } else { + //do nothing + } + } + return 0; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return -1; + } + } else if (codeInteger == 201) { +// System.out.println("201:报销审批,请使用大数据接口获取数据"); + String dataString=""; + try { + dataString = jsonObject.getString("data"); + } catch (JSONException e) { + e.printStackTrace(); + return -1; + } + if (dataString.equals("")) { + //数据为空 + } else { + try { + JSONObject dataJsonObject = null; + dataJsonObject = new JSONObject(dataString); + String dataIdString = dataJsonObject.getString("dataId"); + Integer dataSizeInteger = dataJsonObject.getInt("dataSize"); + Integer sliceNumInteger = dataJsonObject.getInt("sliceNum"); + Integer sliceSizeInteger = dataJsonObject.getInt("sliceSize"); + byte[] noticelistByte = new byte[dataSizeInteger]; + for (Integer sliceNo = 0; sliceNo < sliceNumInteger; sliceNo++) { + // 发送数据请求 + Long seqLongT = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLongT); + System.out.println("getDataSlice: " + mainApp.getGetDataSlice201(dataIdString, sliceNo)); + orderInfo.setJsonString(mainApp.getGetDataSlice201(dataIdString, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/getDataSlice", orderInfo); + // 获取用户列表信息 + ResponseData responseDataCache = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLongT); + byte[] byteCache = responseDataCache.getFileSlice(); + System.out.println("byteCache size:" + byteCache.length); + System.out.println("byteCache:" + byteCache); + if (sliceNo == (sliceNumInteger - 1)) { + for (int x = (sliceNo * sliceSizeInteger); x < dataSizeInteger; x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } else { + for (int x = (sliceNo * sliceSizeInteger); x < (sliceNo * sliceSizeInteger + 7000); x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } + } + String cacheString = null; + try { + cacheString = new String(noticelistByte, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return -1; + } + System.out.println(cacheString); + //解析报销列表 + JSONArray dataJsonArray = new JSONArray(cacheString); + String reimbursementCacheString; + JSONObject reimbursementCacheJsonObject; + // 遍历每个信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + reimbursementCacheString = dataJsonArray.getString(i); + reimbursementCacheJsonObject = new JSONObject(reimbursementCacheString); + Integer id = reimbursementCacheJsonObject.getInt("approvalId"); + String senderInfo = reimbursementCacheJsonObject.getString("proposer"); + // 解析发送者和接收者的姓名 + String receiverInfo = reimbursementCacheJsonObject.getString("approver"); + Integer process = reimbursementCacheJsonObject.getInt("process"); + String detailString = reimbursementCacheJsonObject.getString("detail"); + JSONObject receiverJsonObject = new JSONObject(receiverInfo); + JSONObject senderJsonObject = new JSONObject(senderInfo); + String sender_usernameString = senderJsonObject.getString("username"); + String receiver_usernameString = receiverJsonObject.getString("username"); + // 解析报销详细内容 + System.out.println("报销详细: " + detailString); + detailString = detailString.substring(1, detailString.length() - 1); + System.out.println("处理后的报销详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + Long detail_date = detailJsonObject.getLong("date"); + String detail_content = detailJsonObject.getString("content"); + Integer detail_invoiceNum = detailJsonObject.getInt("invoiceNum"); + Double detail_totalMoney = detailJsonObject.getDouble("totalMoney"); + String detail_detail = detailJsonObject.getString("detail"); + //解析邮件的附件列表 + String filesString = detailJsonObject.getString("pictures"); + JSONArray filesJsonArray = new JSONArray(filesString); + List reimbursementfiles = new LinkedList(); + for (int j = 0; j < filesJsonArray.length(); j++) { + String onefileString = filesJsonArray.getString(j); + reimbursementfiles.add(onefileString); + } + //构建一个报销对象 + Reimbursement reimbursement = new Reimbursement(); + reimbursement.setId(id); + reimbursement.setProcess(process); + reimbursement.setDate(detail_date); + reimbursement.setContent(detail_content); + reimbursement.setDetail(detail_detail); + reimbursement.setNumber(detail_invoiceNum); + reimbursement.setTotalMoney(detail_totalMoney); + reimbursement.setSender(sender_usernameString); + reimbursement.setReceiver(receiver_usernameString); + reimbursement.files = reimbursementfiles; + if (reimbursement.getProcess() == 0) { + reimbursementInfoList.add(reimbursement); + } + } + } catch (JSONException e) { + e.printStackTrace(); + return -1; + } + } + return 0; + } else { + return -1; + } + } + + // 获取邮件记录命令: 0表示查询我发送的,1表示查询发送给我的 + private String getGetApproval(Integer queryCode) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getApproval"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer typeCodeInteger = 2; + Integer queryCodeInteger = queryCode; + Integer pageNoInteger = 0; // 页编号 + Integer pageSizeInteger = 100; // 页大小 + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("typeCode", typeCodeInteger); + jsonObject.put("queryCode", queryCodeInteger); + jsonObject.put("pageNo", pageNoInteger); + jsonObject.put("pageSize", pageSizeInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} diff --git a/src/main/java/cn/minoa/view/home/SingleReimbursementDetailsController.java b/src/main/java/cn/minoa/view/home/SingleReimbursementDetailsController.java new file mode 100644 index 0000000..da5c98c --- /dev/null +++ b/src/main/java/cn/minoa/view/home/SingleReimbursementDetailsController.java @@ -0,0 +1,139 @@ +package cn.minoa.view.home; + +import cn.minoa.MainApp; +import cn.minoa.model.Reimbursement; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; + +import java.io.File; + +public class SingleReimbursementDetailsController { + private MainApp mainApp; + private Stage dialogStage; + + @FXML + private Text senderText; + @FXML + private Text receiverText; + @FXML + private Text processText; + @FXML + private Text moneyText; + @FXML + private Text numberText; + @FXML + private Text contentText; + @FXML + private TextArea detailTextArea; + + @FXML + private Text reimFilesTitleText; + @FXML + private ListView reimFiles; + + public SingleReimbursementDetailsController(){ + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + reimFiles.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> downloadReimFile(newValue.getId())); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + public void setReimInfo(Reimbursement reimbursement) { + senderText.setText(reimbursement.getSender()); + receiverText.setText(reimbursement.getReceiver()); + processText.setText(reimbursement.getProcess().toString()); + moneyText.setText(reimbursement.getTotalMoney().toString()); + numberText.setText(reimbursement.getNumber().toString()); + contentText.setText(reimbursement.getContent()); + detailTextArea.setText(reimbursement.getDetail()); + // 如果邮件带有附件,将其显示到前端 + showReimFiles(reimbursement); + } + + private void showReimFiles(Reimbursement reimbursement) { + if (reimbursement.getFilesNumber() == 0) { + // 没有文件,隐藏附件列表 + // ... + reimFilesTitleText.setText("报销凭证【当前报销申请无附加凭证】"); + } else { + // 有文件,展示到前端 + for (int i = 0; i < reimbursement.getFilesNumber(); i++) { + reimFiles.getItems().add(new reimbursementFileListItem(reimbursement.files.get(i)).getPaneItem()); + } + } + } + + //从服务器下载选中的附件到指定的本地文件夹下 + private void downloadReimFile(String pathfilename) { + //选择要下载到的文件夹 + String localDirString=mainApp.fileLocalPath; + DirectoryChooser directoryChooser=new DirectoryChooser(); + directoryChooser.setTitle("选择要下载到的本地文件夹"); + Stage selectFileStage = new Stage(); + File file = directoryChooser.showDialog(selectFileStage); + if (file != null) { + //文件不为空,且不是文件夹 + if(file.isDirectory()) { + localDirString=file.getPath()+"\\"; + System.out.println("选中的文件夹为:"+localDirString); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("选中类型错误"); + alert.setHeaderText("你的选中类型错误"); + alert.setContentText("请确保你选中的是一个文件夹而不是一个文件。"); + alert.showAndWait(); + } + }else{ + return ; + } + + System.out.println("开始下载... :"+pathfilename); +// int flag=mainApp.downloadFileByPathfilename(pathfilename,localDirString); + int flag=-1; + if(flag==0) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("下载成功"); + alert.setHeaderText("下载文件成功"); + alert.setContentText("您请求的文件数据已经被全部下载到本地," + + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" + + "您的文件保存位置为:"+localDirString); + alert.showAndWait(); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("本地文件数据写入失败,请检查是否有权限访问默认存储位置。" + + "您的文件默认保存位置为:"+localDirString); + alert.showAndWait(); + } + } + + public void setDialogStage(Stage stage) { + this.dialogStage = stage; + } + + @FXML + private void exit() { + this.dialogStage.close(); + } +} diff --git a/src/main/java/cn/minoa/view/home/WrittenReimbursementController.java b/src/main/java/cn/minoa/view/home/WrittenReimbursementController.java new file mode 100644 index 0000000..4069205 --- /dev/null +++ b/src/main/java/cn/minoa/view/home/WrittenReimbursementController.java @@ -0,0 +1,466 @@ +package cn.minoa.view.home; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.Person; +import cn.minoa.view.login.LoginOverviewController; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class WrittenReimbursementController { + + private MainApp mainApp; + private Stage dialogStage; + + @FXML + private TextField receiverField; + @FXML + private TextField moneyField; + @FXML + private TextField numberField; + @FXML + private TextField contentField; + @FXML + private TextArea detailArea; + + @FXML + private ListView reimbursementFiles; + //与之对应的数据结构 + private List reimbursementFilesList; + + public WrittenReimbursementController(){ + reimbursementFilesList=new LinkedList(); + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + receiverField.setPromptText("请输入一个联系人..."); + moneyField.setPromptText("请输入一个正数..."); + numberField.setPromptText("请输入一个不小于0的整数..."); + contentField.setPromptText("请输入报销类别,如:采购经费、活动经费等..."); + detailArea.setPromptText("请输入报销费用的详细描述..."); + } + + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + public void setStage(Stage stage) { + this.dialogStage = stage; + } + + /* + * 从弹窗获取要添加的附件文件路径,将其加入到附件列表中,并显示到前端 + */ + @FXML + private void addFilesToReimbursement() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择上传文件"); + Stage selectFileStage = new Stage(); + File file = fileChooser.showOpenDialog(selectFileStage); + if (file != null) { + //文件不为空,且不是文件夹 + if(file.isFile()) { + //判断文件长度是否为0 + System.out.println("file length: "+file.length()); + if(file.length()==0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("空文件错误"); + alert.setHeaderText("您选择的是一个空文件"); + alert.setContentText("您选择的是一个空文件,请重新选择。"); + alert.showAndWait(); + return ; + } + if(!isSelected(file.getAbsolutePath())) { + reimbursementFilesList.add(file.getAbsolutePath()); + reimbursementFiles.getItems().add(new reimbursementFileListItem(file.getAbsolutePath()).getPaneItem()); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("附件添加重复"); + alert.setHeaderText("你选择的该文件已加入附件列表"); + alert.setContentText("请确保你选中的文件是未加入附件列表的文件。"); + alert.showAndWait(); + } + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("附件类型错误"); + alert.setHeaderText("你的文件类型错误"); + alert.setContentText("请确保你选中的是一个文件而非文件夹。"); + alert.showAndWait(); + } + } + } + + //根据文件路径判断该选中文件是否已经加入到列表中 + private boolean isSelected(String absolutePath) { + boolean flag=false; + for(int i=0;i persons = mainApp.contactListData; + for (Iterator iterator = persons.iterator(); iterator.hasNext(); ) { + Person person = (Person) iterator.next(); + if (person.getUserName().equals(receiverName)) { + flag++; + } + } + if (flag == 0) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("收件人错误"); + alert.setHeaderText("你的收件人错误"); + alert.setContentText("请确保你输入的收件人姓名是系统的实际用户。"); + alert.showAndWait(); + return false; + } + //验证金额 + if (moneyField.getText().trim().equals("")) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("报销金额错误"); + alert.setHeaderText("你的报销金额错误"); + alert.setContentText("请确保你输入的报销金额不为空。"); + alert.showAndWait(); + return false; + } else { + try { + Double money=Double.parseDouble(moneyField.getText()); + if(money<=0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("报销金额错误"); + alert.setHeaderText("你的报销金额错误"); + alert.setContentText("请确保你输入的报销金额是一个正数。"); + alert.showAndWait(); + return false; + } + } catch (NumberFormatException e) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("报销金额错误"); + alert.setHeaderText("你的报销金额错误"); + alert.setContentText("请确保你输入的报销金额是一个数字。"); + alert.showAndWait(); + return false; + } + } + //验证单据 + if (numberField.getText().trim().equals("")) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("单据张数错误"); + alert.setHeaderText("你的单据张数错误"); + alert.setContentText("请确保你输入的单据张数不为空。"); + alert.showAndWait(); + return false; + } else { + try { + Integer number=Integer.parseInt(numberField.getText()); + if(number<0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("单据张数错误"); + alert.setHeaderText("你的单据张数错误"); + alert.setContentText("请确保你输入的单据张数不小于0。"); + alert.showAndWait(); + return false; + } + } catch (NumberFormatException e) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("单据张数错误"); + alert.setHeaderText("你的单据张数错误"); + alert.setContentText("请确保你输入的单据张数是一个整数。"); + alert.showAndWait(); + return false; + } + } + //验证报销类别 + if (contentField.getText().trim().equals("")) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("报销内容错误"); + alert.setHeaderText("你的报销内容错误"); + alert.setContentText("请确保你输入的报销内容不为空。"); + alert.showAndWait(); + return false; + } + //验证报销详情 + if (detailArea.getText().trim().equals("")) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("费用详细错误"); + alert.setHeaderText("你的费用详细错误"); + alert.setContentText("请确保你输入的费用详细不为空。"); + alert.showAndWait(); + return false; + } + return true; + } + + //发送报销命令 + private String getSendApproval () { + boolean flag=true; //记录上传附件是否成功 + JSONObject detailJsonObject = new JSONObject(); + String detail_detailString = detailArea.getText().trim(); + String detail_contentString = contentField.getText().trim(); + Integer detail_numberInteger = Integer.parseInt(numberField.getText().trim()); + Double detail_moneyDouble = Double.parseDouble(moneyField.getText().trim()); + String detail_approvalER=receiverField.getText().trim(); + Long detail_date=LoginOverviewController.getNowTimeStamp(); + //根据已经添加的文件附件列表制作一个arraylist + JSONArray filesArray = new JSONArray(); + for (int i = 0; i < reimbursementFilesList.size(); i++) { + String pathfilename = reimbursementFilesList.get(i); + //上传该文件到服务器共享文件夹目录下 + File file = new File(pathfilename); +// Integer res=mainApp.uploadFileToServerSharedFolder(file); + Integer res=-1; + if(res<0){ + flag=false; + } + //将该文件信息[服务器文件路径地址]写入邮件信息中 + String oneFile = mainApp.sharedFileServerPath + getPureFileNameFromWindows(pathfilename); + filesArray.put(oneFile); + } + try { + detailJsonObject.put("date", detail_date); + detailJsonObject.put("content", detail_contentString); + detailJsonObject.put("invoiceNum",detail_numberInteger); + detailJsonObject.put("totalMoney",detail_moneyDouble); + detailJsonObject.put("detail",detail_detailString); + detailJsonObject.put("approvalER",detail_approvalER); + detailJsonObject.put("pictures", filesArray); + } catch (JSONException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + JSONObject jsonObject = new JSONObject(); + String commandString = "/sendApproval"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String approverString = receiverField.getText().trim(); + Integer typeCodeInteger = 2; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("approver", approverString); + jsonObject.put("typeCode", typeCodeInteger); + jsonObject.put("detail", detailJsonObject.toString()); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + if(flag==false){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("邮件附件错误"); + alert.setHeaderText("你的邮件附件错误"); + alert.setContentText("由于邮件附件过大或权限问题,你的邮件附件不能被上传至服务器,请检查之。"); + alert.showAndWait(); + return ""; + } + return jsonObject.toString(); + } + + // 根据包含路径的文件名获取纯文件名[windows本地] + private String getPureFileNameFromWindows(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '\\') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[windows]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[windows]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } + +} + +//报销附件 +class reimbursementFileListItem { + // 外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + // 头像 + private Button head; + private Integer headLength; + // 文本区域 +// public TextArea text; + private Text text; +// private Integer textWidth; +// private Integer textHeight; + + // 每个Pane代映射了一个文件[路径+文件名+@文件类型] + String pathfilename; + + // 组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + + // 设置联系人头像 + private void setHeadPortrait(Button button, String filetype) { + button.setStyle( + String.format("-fx-background-image: url('file:resources/images/emailFileIcon/%s.png')", filetype)); + } + + // 构造函数 + public reimbursementFileListItem(String pathfiletypename) { + // 映射数据 + this.pathfilename = pathfiletypename; + // 大小配置 + paneWidth = 200; + paneHeight = 50; + headLength = 35; +// textWidth = 150; +// textHeight = 25; + // 位置配置 + headXInteger = 10; + headYInteger = 10; + textXInteger = 70; + textYInteger = 28; + + pane = new Pane(); + head = new Button(); + text = new Text(); + + // 最外面的pane的大小 + pane.setPrefSize(paneWidth, paneHeight); + + // 设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + + // 设置消息头像和消息文本属性 + head.setPrefSize(headLength, headLength); + } + + // 根据person获取pane + public Pane getPaneItem() { + String name = this.pathfilename; + // 头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + // text位置 + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + // 头像和气泡文本内容 + text.setText(name); + setHeadPortrait(head, "f"); + pane.getChildren().add(head); + pane.getChildren().add(text); + // 设置pane的id + pane.setId(name); + return pane; + } +} diff --git a/src/main/java/cn/minoa/view/login/KeepLoginStatus.java b/src/main/java/cn/minoa/view/login/KeepLoginStatus.java new file mode 100644 index 0000000..472f26f --- /dev/null +++ b/src/main/java/cn/minoa/view/login/KeepLoginStatus.java @@ -0,0 +1,162 @@ +package cn.minoa.view.login; + +import cn.minoa.Main; +import cn.minoa.MainApp; +import cn.minoa.model.Loginer; +import cn.minoa.util.EncryptionUtil; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +// 维持长登录状态 +public class KeepLoginStatus { + + public static void setLoginStatus(boolean flag){ + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + if(flag==true){ + settingProperties.setProperty("LoginStatus","true"); + }else{ + settingProperties.setProperty("LoginStatus","false"); + } + outputStream=new BufferedOutputStream(new FileOutputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"修改配置文件"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static void setLoginInfo(Loginer loginer){ + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty("loginUserName",EncryptionUtil.encryptBasedAES(loginer.getUserName())); + settingProperties.setProperty("loginPassWord",EncryptionUtil.encryptBasedAES(loginer.getPassWord())); + outputStream=new BufferedOutputStream(new FileOutputStream(MainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(outputStream,StandardCharsets.UTF_8)); + settingProperties.store(bw,"修改配置文件"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static Loginer getLoginerInfo(){ + Loginer m_loginer=new Loginer(); + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream(MainApp.getClientPath() + File.separator + "ndn" + File.separator + "settings.properties")); +// inputStream = MainApp.class.getClassLoader().getResourceAsStream("/ndn/settings.properties"); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + String username = EncryptionUtil.decryptBasedAES(settingProperties.getProperty("loginUserName")); + String password = EncryptionUtil.decryptBasedAES(settingProperties.getProperty("loginPassWord")); +// System.out.println("username: " + username); +// System.out.println("password: " + password); + m_loginer.setUserName(username); + m_loginer.setPassWord(password); + } catch (IOException e) { + e.printStackTrace(); + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return m_loginer; + } + + public static boolean getLogineStatus(){ + InputStream inputStream = null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream = new BufferedInputStream(new FileInputStream(MainApp.getClientPath() + File.separator + "ndn" + File.separator + "settings.properties")); +// inputStream = MainApp.class.getClassLoader().getResourceAsStream("/ndn/settings.properties"); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + String LoginStatus = settingProperties.getProperty("LoginStatus"); + if(LoginStatus.equals("true")){ + return true; + }else{ + return false; + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } finally { + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cn/minoa/view/login/LoginOverviewController.java b/src/main/java/cn/minoa/view/login/LoginOverviewController.java new file mode 100644 index 0000000..9320d22 --- /dev/null +++ b/src/main/java/cn/minoa/view/login/LoginOverviewController.java @@ -0,0 +1,1464 @@ +package cn.minoa.view.login; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import cn.minoa.dataRequestInterface.*; +import cn.minoa.model.Loginer; +import cn.minoa.util.*; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.pib.Pib; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.security.tpm.Tpm; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.v2.CertificateV2; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.model.Person; +import cn.minoa.model.SingleMessage; +import cn.minoa.model.SingleNotice; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; + +public class LoginOverviewController { + + private MainApp mainApp; + private OrderInfo orderInfo; + + public LoginOverviewController() { + orderInfo = new OrderInfo(); + } + + @FXML + private TextField userNameFiled; + @FXML + private PasswordField passwordFiled; + @FXML + private ImageView loginHeadPhoto; + @FXML + private ImageView miniLogo; + @FXML + private ImageView settingImage; + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + miniLogo.setImage(new Image("file:resources/images/logo_mini.png")); + settingImage.setImage(new Image("file:resources/images/setting_gear.png")); + loginHeadPhoto.setImage(new Image("file:resources/images/ic_launcher-web.png")); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + @FXML + private void handleSetting() { + // 弹出注册界面 + mainApp.showSettingOverview(); + } + + @FXML + private void handleRegister() { + // 初始化时间戳差值 + MainApp.iniTimeStampBias(); + System.out.println("timeStampBias: "+MainApp.timeStampBias); + // 弹出注册界面 + mainApp.showRegisterOverview(); + } + + @FXML + private void handleRegisterByFlip() { + // 从登录页翻转到注册界面 + mainApp.showRegisterOverviewByFlip(); + } + + @FXML + private void handleFindPassword() { + // 找回密码 + // 。。。 + } + + @FXML + private void handleLogin() throws Exception { + // 实例化 +// mainApp.minoaDataAPI = new MinoaDataAPI(mainApp); + String userNameString = userNameFiled.getText().trim(); + String passWordString = passwordFiled.getText().trim(); + if (userNameString.equals("")) { + return; + } + // 登录之前先查看本地是否有该用户拥有的证书 + String keyChainNameString = "/" + userNameString; + try { + if (!KeyManager.INSTANCE.isInKeyChain(keyChainNameString)) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("密钥错误"); + alert.setHeaderText("本地没有你的密钥"); + alert.setContentText("请导入密钥/证书。"); + alert.showAndWait(); + return; + } + } catch (Pib.Error | PibImpl.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + return; + } + // 每次手动登录之前,将keyChain初始化一下 + MinoaDataAPI.keyChainNameString = keyChainNameString; + try { + KeyManager.INSTANCE.initKeyChain(keyChainNameString); + } catch (Pib.Error | PibImpl.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + } + // 登录验证 +// String loginString = getLoginJsonString(); + String loginString= null; + try { + loginString = getLoginJsonRSAString(); + } catch (Exception e) { + System.out.print("rsa加密失败:"+e.getMessage()); + return ; + } + boolean isRight = checkPass(loginString); + if (isRight) { + // 持久化保存当前用户名和密码 + keepLoginInfo(true, userNameString, passWordString); + // 持久化保存当前keyName + KeyStorageUtil.setKeyName(keyChainNameString); + // 修改登录状态 + mainApp.isNeedKnowLoginStatus = true; + mainApp.iniShow(); + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("用户名或密码错误"); + alert.setHeaderText("请核对用户名或密码"); + alert.setContentText("请核对用户名或密码。\n" + + "如果你故意导入了首字段与用户名相同的密钥证书,但" + + "该证书是非法生成的,也将弹出该弹窗!!!"); + alert.showAndWait(); + } + } + +// public boolean checkPass(byte[] loginJsonString) { +// System.out.println("checking your password..."); +// boolean flag = false; +// // 发送数据请求 +// Long seqLong = mainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +//// orderInfo.setJsonString(loginJsonString); +// orderInfo.setJsonByte(loginJsonString); +// mainApp.minoaDataAPI.executeOrder("/login", orderInfo, true); +// // 根据结果判断是否登录成功 +// ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// responseData.printSelf(); +// // 解密 +//// responseData.setResponseData(AESCrypto.decryptBasedAES(responseData.getResponseJsonDataString())); +// // 解析jsons +// JSONObject jsonObject = responseData.praseRequestData(); +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// System.out.println("login 200"); +// flag = true; +// // 初始化Loginer的信息 +// updateLoginer(jsonObject); +// System.out.println("ini loginer (include the head photo) success"); +// // 初始化其它系统信息 +// iniLoginerData(); +// // 打印输出 +// mainApp.loginer.printSelf(); +// } +// return flag; +// } + + public boolean checkPass(String loginJsonString) throws Exception { + // 初始化时间戳差值 + MainApp.iniTimeStampBias(); + System.out.println("timeStampBias: "+MainApp.timeStampBias); + + System.out.println("checking your password..."); + boolean flag = false; + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(loginJsonString); + mainApp.minoaDataAPI.executeOrder("/login", orderInfo); + // 根据结果判断是否登录成功 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + responseData.printSelf(); + // 解密 +// Base64.Encoder base64Encoder = Base64.getMimeEncoder(); +// String dataContentBase64 = base64Encoder.encodeToString(responseData.getResponseJsonDataString()); +// responseData.setResponseData(Aes.Decrypt()); +// responseData.printSelf(); + // 解析jsons + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + System.out.println("login 200"); + flag = true; + // 初始化Loginer的信息 + updateLoginer(jsonObject); + System.out.println("ini loginer (include the head photo) success"); + // 初始化其它系统信息 + iniLoginerData(); + // 打印输出 + mainApp.loginer.printSelf(); + } + return flag; + } + + // 初始化登录者的一些系统信息 + private void iniLoginerData() { + // 初始化联系人列表信息及其头像 +// updateContactList(); +// System.out.println("ini contact list (include the head photos) success"); +// // 初始化与所有联系人的消息记录列表 +// updateMessageLog(); +// System.out.println("ini message log success"); +// // 初始化通知列表 +// updateNoticeList(); +// System.out.println("ini notice success"); + // 开启[消息列表/通知/审批]的线程循环监听 + System.out.println("Creating Thread to send Heart Beat Packet..."); + mainApp.creatThreadToRefreshMessageLog(); + System.out.println("Created Thread to send Heart Beat Packet."); + } + + // 请求服务器某文件的大小:返回-1表示没有该文件或登录失效 + public Long getServerFileLength(String filename) { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + if (filename.contains("/")) { + orderInfo.setJsonString(getFileInfoJson(filename)); + mainApp.minoaDataAPI.executeOrder("/fileInfo", orderInfo); + } else { + orderInfo.setJsonString(getFileShareInfoJson(filename)); + mainApp.minoaDataAPI.executeOrder("/fileShareInfo", orderInfo); + } + // 获取头像信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + System.out.println("getServerFileLength-fileInfo-JSON: " + jsonObject.toString()); + responseData.printSelf(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + JSONObject dataJsonObject = new JSONObject(dataString); + Long fileSize = dataJsonObject.getLong("fileSize"); + return fileSize; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return (long) -1; + } + + // 导入密钥 + @FXML + private void importSafeBag() { +// SecretKeyManageUtil.testSecretKey(KeyStorageUtil.getKeyName()); +// System.out.println("#####test end#####"); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择密钥文件"); + Stage selectFileStage = new Stage(); + File file = fileChooser.showOpenDialog(selectFileStage); + if (file != null) { + // 文件不为空,且不是文件夹 + if (file.isFile()) { + //判断文件长度是否为0 + System.out.println("file length: " + file.length()); + if (file.length() == 0) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("空文件错误"); + alert.setHeaderText("您选择的是一个空文件"); + alert.setContentText("您选择的是一个空文件,请重新选择。"); + alert.showAndWait(); + return; + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件类型错误"); + alert.setHeaderText("你的文件类型错误"); + alert.setContentText("请确保你选中的是一个文件而非文件夹。"); + alert.showAndWait(); + return; + } + } else { + return; + } + // 将所选文件导入密钥 + String res = SecretKeyManageUtil.importSafeBag(file.getAbsolutePath()); + if (res.equals("success")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("密钥导入成功"); + alert.setHeaderText("密钥导入成功"); + alert.setContentText("你已成功导入密钥"); + alert.showAndWait(); + } else { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("密钥导入失败"); + alert.setHeaderText("密钥导入失败"); + alert.setContentText("由于如下原因,密钥导入时出现了问题:\n" + res); + alert.showAndWait(); + } + } + + // 下载文件[返回0:已经成功接收所有回执数据并写入本地文件] + public Integer downloadFile(String filename) { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileInfoJson(filename)); + mainApp.minoaDataAPI.executeOrder("/fileInfo", orderInfo); + // 获取头像信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + System.out.println("fileInfo-JSON: " + jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + JSONObject dataJsonObject = new JSONObject(dataString); + Integer sliceNumInteger; + sliceNumInteger = dataJsonObject.getInt("sliceNum"); + // 根据分片信息获取每个分片,并合并成一个完整比特流 + Integer flagInteger = 0; + for (int i = 0; i < sliceNumInteger; i++) { + flagInteger += downLoadFileBySlice(filename, i); + } + System.out.println("下载分片的消息回执[0表示全部数据分片已经被写入本地]: " + flagInteger); + return flagInteger; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return 1; + } + } + return 1; + } + + /* + * @param filename 要下载的文件的“服务器路径+文件名称”,即写入请求的名称 + * + * @param localDir 要下载文件到的本地地址 + * + * @param localName 要下载的文件重命名之后的本地名称 + * + * @return 0表示已经成功接收所有回执数据并写入本地文件,否则出现error + * + * 目的是下载头像到指定路径,并重命名之。 + */ + private Integer downloadRenamedFileToSpecifiedDir(String filename, String localDir, String localName) { + Long filelen = getServerFileLength(filename); + Integer sliceNum = (int) (filelen / mainApp.sliceSize); + if (filelen % mainApp.sliceSize != 0) { + sliceNum++; + } + Long startTime = System.currentTimeMillis(); + DownLoadClientBPRenewal downLoadClient = new DownLoadClientBPRenewal(filename, + localDir, sliceNum, mainApp.threadNumberForFileTransfer); + // 下载文件 + boolean res = downLoadClient.downloadData(); + Long endTime = System.currentTimeMillis(); + System.out.println("耗时:" + (endTime - startTime) + "ms"); + // 每个分片都下载成功 + if (res) { + // 重命名该文件 + String fromPathName = localDir + + MinoaDataAPI.getPureFileName(filename); + String toPathName = localDir + localName; + renameLocalFile(fromPathName, toPathName); + return 0; + } else { + return -1; + } + } + + // 重命名本地文件 + private void renameLocalFile(String fromPathName, String toPathName) { + File file = new File(fromPathName); + if (file.exists()) { + File toPathNameFile = new File(toPathName); + file.renameTo(toPathNameFile); + } else { + System.out.println("要重命名的文件不存在"); + } + } + + // 根据包含路径的文件名获取纯文件名[linux下] + public String getPureFileName(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '/') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[linux]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[linux]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } + + // 根据包含路径的文件名获取纯文件名 +// public String getPureFileName(String pathfilename) { +// // 文件名称切割 +// while (pathfilename.indexOf('/') > 0) { +// pathfilename = pathfilename.substring(pathfilename.indexOf('/') + 1, pathfilename.length()); +// } +// System.out.println("pure file name: " + pathfilename); +// return pathfilename; +// } + + // 合并两个Byte[] +// public Byte[] byteMerger(Byte[] bt1, Byte[] bt2){ +// Byte[] bt3 = new Byte[bt1.length+bt2.length]; +// System.arraycopy(bt1, 0, bt3, 0, bt1.length); +// System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length); +// return bt3; +// } + + // 请求文件分片数据=>返回0表示分片数据被成功下载 + private Integer downLoadFileBySlice(String filename, Integer sliceNo) { + Integer flagInteger; + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getDownloadFileJson(filename, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/downloadFile", orderInfo); + // 获取头像信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// JSONObject jsonObject=responseData.praseRequestData(); + System.out.println("downloadFileSlice-JSON: " + responseData.getResponseJsonDataString()); + flagInteger = Integer.parseInt(responseData.getResponseJsonDataString()); + return flagInteger; + } + + // 获取要下载的共享文件信息 + public String getFileShareInfoJson(String filename) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/fileShareInfo"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 获取要下载的文件信息 + public String getFileInfoJson(String filename) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/fileInfo"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 获取要下载的共享文件信息 + private String getDownloadShareFileJson(String filename, Integer sliceNo) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/downloadShareFile"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + Integer sliceNoInteger = sliceNo; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("sliceNo", sliceNoInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + System.out.println("getDownloadShareFileJson: " + jsonObject.toString()); + return jsonObject.toString(); + } + + // 获取要下载的文件信息 + private String getDownloadFileJson(String filename, Integer sliceNo) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/downloadFile"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + Integer sliceNoInteger = sliceNo; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("sliceNo", sliceNoInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + public String getLoginJsonString(String userNameString, String passWordString) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/login"; + // 对密码进行哈希加密 + passWordString = EncryptionUtil.getMD5String(passWordString); + mainApp.loginer.setUserName(userNameString); + mainApp.loginer.setPassWord(passWordString); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 从前端界面获得登录数据并转换成json格式 + private String getLoginJsonString() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/login"; + String userNameString = userNameFiled.getText().trim(); + String passWordString = passwordFiled.getText().trim(); + if (userNameString == null || passWordString == null) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("UserName or Password is null"); + alert.setHeaderText("Please correct UserName or Password."); + alert.setContentText("Please correct UserName or Password."); + alert.showAndWait(); + } else { + // 保存登录信息到本地 +// keepLoginInfo(true,userNameString,passWordString); + } + // 对密码进行哈希加密 + passWordString = EncryptionUtil.getMD5String(passWordString); + mainApp.loginer.setUserName(userNameString); + mainApp.loginer.setPassWord(passWordString); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + public String getLoginJsonRSAString(String userNameString, String passWordString) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/login"; + // 对密码进行哈希加密 + passWordString = EncryptionUtil.getMD5String(passWordString); + mainApp.loginer.setUserName(userNameString); + mainApp.loginer.setPassWord(passWordString); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + jsonObject.put("aesKey", Aes.getDefaultKey()); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + // 用rsa公钥进行加密 + String jsonString = jsonObject.toString(); + System.out.println("rsa加密前:" + jsonString); + String rsaString = null; + try { + rsaString = Rsa.encrypt(jsonString); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("rsa加密后:" + rsaString); + return rsaString; + } + + // 从前端界面获得登录数据并转换成json格式 + // 增加aesKey字段,然后将整个json用rsa公钥加密 + private String getLoginJsonRSAString() throws Exception { + JSONObject jsonObject = new JSONObject(); + String commandString = "/login"; + String userNameString = userNameFiled.getText().trim(); + String passWordString = passwordFiled.getText().trim(); + if (userNameString == null || passWordString == null) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("UserName or Password is null"); + alert.setHeaderText("Please correct UserName or Password."); + alert.setContentText("Please correct UserName or Password."); + alert.showAndWait(); + } else { + // 保存登录信息到本地 +// keepLoginInfo(true,userNameString,passWordString); + } + // 对密码进行哈希加密 + passWordString = EncryptionUtil.getMD5String(passWordString); + mainApp.loginer.setUserName(userNameString); + mainApp.loginer.setPassWord(passWordString); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + jsonObject.put("aesKey", Aes.getDefaultKey()); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + // 用rsa公钥进行加密 + String jsonString = jsonObject.toString(); + System.out.println("rsa加密前:" + jsonString); + String rsaString = Rsa.encrypt(jsonString); + System.out.println("rsa加密后:" + rsaString); + return rsaString; + } + + // 将账号密码保存在本地 + private void keepLoginInfo(boolean flag, String username, String passord) { + Loginer m_loginer = new Loginer(); + m_loginer.setUserName(username); + m_loginer.setPassWord(passord); + KeepLoginStatus.setLoginStatus(flag); + KeepLoginStatus.setLoginInfo(m_loginer); + } + + // 根据请求到的json数据更新loginer的信息 + private void updateLoginer(JSONObject loginerJsonObject) { + String dataString; + try { + dataString = loginerJsonObject.getString("data"); + System.out.println("dataString: " + dataString); + JSONObject dataJsonObject = new JSONObject(dataString); + String uuid = dataJsonObject.getString("uuid"); + String realname = dataJsonObject.getString("realName"); + String prefix = dataJsonObject.getString("prefix"); + String phone = dataJsonObject.getString("phone"); +// System.out.println("realname: "+realname); +// System.out.println("prefix: "+prefix); + mainApp.loginer.setUuid(uuid); + mainApp.loginer.setRealName(realname); + mainApp.loginer.setPrefix(prefix); + mainApp.loginer.setPhone(phone); + // 初始化登录者头像 + String avatarString = dataJsonObject.getString("avatar"); + System.out.println("avator path: " + avatarString); + if (!isHaveHeadPhoto(mainApp.loginer.getUserName() + ".png")) { + System.out.println("downloading " + mainApp.loginer.getUserName() + "'s head photo file..."); + downloadRenamedFileToSpecifiedDir(avatarString, mainApp.userHeadPhotoBinPath, mainApp.loginer.getUserName() + ".png"); + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 拿到获取用户列表的json格式字符串 + private String getGetUsersJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getUsers"; + String userNameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("uuid", uuidString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 初始化联系人列表信息 + private void updateContactList() { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetUsersJson()); + mainApp.minoaDataAPI.executeOrder("/getUsers", orderInfo); + // 获取用户列表信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); +// System.out.println("getUsers-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + JSONArray dataJsonArray = new JSONArray(dataString); + String userCacheString; + JSONObject userCacheJsonObject; + String userNameCacheString; +// System.out.println("data: "+dataString); + // 遍历每个用户联系人,将其加入mainApp的联系人列表中 + for (int i = 0; i < dataJsonArray.length(); i++) { + userCacheString = dataJsonArray.getString(i); + userCacheJsonObject = new JSONObject(userCacheString); + userNameCacheString = userCacheJsonObject.getString("username"); +// System.out.println("user-"+i+": "+userNameCacheString); + mainApp.contactListData.add(new Person(userNameCacheString)); + // 初始化联系人头像到本地=》目前接口没返回头像路径 + String avatarString = userCacheJsonObject.getString("avatar"); + if (!isHaveHeadPhoto(userNameCacheString + ".png")) { + System.out.println("downloading " + userNameCacheString + "'s head photo file..."); + downloadRenamedFileToSpecifiedDir(avatarString, mainApp.userHeadPhotoBinPath, userNameCacheString + ".png"); + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + //判断是否已经有本地头像 + boolean isHaveHeadPhoto(String headPhotoString) { + File file = new File(mainApp.userHeadPhotoBinPath + headPhotoString); + if (file.exists()) { + System.out.println(headPhotoString + " has existed."); + return true; + } else { + return false; + } + } + + // 请求通知消息 + private String getGetInformJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getInform"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer pageNoInteger = 0; + Integer pageSizeInteger = 100; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("pageNo", pageNoInteger); + jsonObject.put("pageSize", pageSizeInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 请求每个通知的数据分片的JSON + public String getGetDataSlice(String dataId, Integer sliceNo) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getDataSlice"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer sliceNoInteger = sliceNo; + String dataIdString = dataId; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("dataId", dataIdString); + jsonObject.put("sliceNo", sliceNoInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //重新加载所有通知列表信息 + public void reloadNoticeList() { + mainApp.noticeList.clear(); + updateNoticeList(); + } + + // 初始化通知列表信息 + private void updateNoticeList() { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetInformJson()); + mainApp.minoaDataAPI.executeOrder("/getInform", orderInfo); + // 获取用户列表信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + System.out.println("通知答复:"); + responseData.printSelf(); + JSONObject jsonObject = responseData.praseRequestData(); + System.out.println("getInform-JSON: " + jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if (dataString.equals("")) { + //数据为空 + } else { + JSONArray dataJsonArray = new JSONArray(dataString); + String singleNoticeCacheString; + JSONObject singleNoticeCacheJsonObject; + Integer noticeIdInteger; + String usernameCacheString; + String titleCacheString; + String contentCacheString; + String starttimeCacheString; + String endtimeCacheString; + String attachementCacheString; + // 遍历每个用户联系人,将其加入mainApp的联系人列表中 + for (int i = 0; i < dataJsonArray.length(); i++) { + singleNoticeCacheString = dataJsonArray.getString(i); + singleNoticeCacheJsonObject = new JSONObject(singleNoticeCacheString); + //拿到通知ID + noticeIdInteger = singleNoticeCacheJsonObject.getInt("id"); + // 拿到新闻发布人 + usernameCacheString = singleNoticeCacheJsonObject.getString("proposer"); + // 拿到content +// JSONObject detailJsonObject = singleNoticeCacheJsonObject.getJSONObject("detail"); + String detailString = singleNoticeCacheJsonObject.getString("detail"); + System.out.println("通知详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + contentCacheString = detailJsonObject.getString("content"); + // 拿到title + titleCacheString = detailJsonObject.getString("title"); + // 拿到starttime、endtime + starttimeCacheString = detailJsonObject.getString("startTime"); + endtimeCacheString = detailJsonObject.getString("endTime"); + // 拿到attachment + attachementCacheString = detailJsonObject.getString("attachment"); +// System.out.println("user-"+i+": "+usernameCacheString); + // 新建一个notice + SingleNotice singleNotice = new SingleNotice(); + singleNotice.setNoticeId(noticeIdInteger); + singleNotice.setContent(contentCacheString); + singleNotice.setUsername(usernameCacheString); + singleNotice.setTitle(titleCacheString); + singleNotice.setStarttime(starttimeCacheString); + singleNotice.setEndtime(endtimeCacheString); + singleNotice.setAttachment(attachementCacheString); + mainApp.noticeList.add(singleNotice); + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else if (codeInteger == 201) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if (dataString.equals("")) { + //数据为空 + } else { + JSONObject dataJsonObject = new JSONObject(dataString); + String dataIdString = dataJsonObject.getString("dataId"); + Integer dataSizeInteger = dataJsonObject.getInt("dataSize"); + Integer sliceNumInteger = dataJsonObject.getInt("sliceNum"); + Integer sliceSizeInteger = dataJsonObject.getInt("sliceSize"); + byte[] noticelistByte = new byte[dataSizeInteger]; + for (Integer sliceNo = 0; sliceNo < sliceNumInteger; sliceNo++) { + // 发送数据请求 + Long seqLongT = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLongT); + System.out.println("getDataSlice: " + getGetDataSlice(dataIdString, sliceNo)); + orderInfo.setJsonString(getGetDataSlice(dataIdString, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/getDataSlice", orderInfo); + // 获取用户列表信息 + ResponseData responseDataCache = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLongT); + byte[] byteCache = responseDataCache.getFileSlice(); + System.out.println("byteCache size:" + byteCache.length); + System.out.println("byteCache:" + byteCache); + if (sliceNo == (sliceNumInteger - 1)) { + for (int x = (sliceNo * sliceSizeInteger); x < dataSizeInteger; x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } else { + for (int x = (sliceNo * sliceSizeInteger); x < (sliceNo * sliceSizeInteger + 7000); x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } + } + try { + String cacheString = new String(noticelistByte, "UTF-8"); + System.out.println(cacheString); + //解析通知列表 + JSONArray dataJsonArray = new JSONArray(cacheString); + String singleNoticeCacheString; + JSONObject singleNoticeCacheJsonObject; + String usernameCacheString; + String titleCacheString; + String contentCacheString; + String starttimeCacheString; + String endtimeCacheString; + String attachementCacheString; + // 遍历每个用户联系人,将其加入mainApp的联系人列表中 + for (int i = 0; i < dataJsonArray.length(); i++) { + singleNoticeCacheString = dataJsonArray.getString(i); + singleNoticeCacheJsonObject = new JSONObject(singleNoticeCacheString); + // 拿到新闻发布人 + usernameCacheString = singleNoticeCacheJsonObject.getString("proposer"); + // 拿到content + String detailString = singleNoticeCacheJsonObject.getString("detail"); + JSONObject detailJsonObject = new JSONObject(detailString); + contentCacheString = detailJsonObject.getString("content"); + // 拿到title + titleCacheString = detailJsonObject.getString("title"); + // 拿到starttime、endtime + starttimeCacheString = detailJsonObject.getString("startTime"); + endtimeCacheString = detailJsonObject.getString("endTime"); + // 拿到attachment + attachementCacheString = detailJsonObject.getString("attachment"); +// System.out.println("user-"+i+": "+usernameCacheString); + // 新建一个notice + SingleNotice singleNotice = new SingleNotice(); + singleNotice.setContent(contentCacheString); + singleNotice.setUsername(usernameCacheString); + singleNotice.setTitle(titleCacheString); + singleNotice.setStarttime(starttimeCacheString); + singleNotice.setEndtime(endtimeCacheString); + singleNotice.setAttachment(attachementCacheString); + mainApp.noticeList.add(singleNotice); + } + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + // 初始化与所有联系人的往来消息列表 + public void updateMessageLog() { + System.out.println("ini message log start..."); + // 清空旧的联系人消息清单 + mainApp.messageLogMap.clear(); + // 获取联系人清单 + ObservableList contactList = mainApp.getContactListData(); + // 遍历联系人,获取与他们的聊天消息记录 + for (Person person : contactList) { + // 新建当前用户的消息记录列表 + ObservableList messageLogByPersonCacheList = FXCollections.observableArrayList(); + mainApp.messageLogMap.put(person.getUserName(), messageLogByPersonCacheList); + // 赋值该消息记录列表(当前时间之前的五条消息记录) + fullMessageLogByPerson(person); + } + // 为了容错"/getNews"轮询到自己发出的消息,这里在消息记录表中增加一个自己 + ObservableList messageLogByMyselfList = FXCollections.observableArrayList(); + mainApp.messageLogMap.put(mainApp.loginer.getUserName(), messageLogByMyselfList); + // 赋值登录时未读消息记录列表(循环请求getNews,并将联系人头像标红) + System.out.println("fullIniNotReadMessageListWhenLoginByGetNews-start"); + for (int i = 0; i < 1; i++) { // 只请求一次getNews有时候拿不到新消息,这里多发送几次请求。 + fullNotReadMessageListByGetNews(); + } + System.out.println("fullIniNotReadMessageListWhenLoginByGetNews-end"); + } + + // 赋值该消息记录列表(循环请求getNews,并将联系人头像标红) + private void fullNotReadMessageListByGetNews() { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetNewsJson()); + mainApp.minoaDataAPI.executeOrder("/getNews", orderInfo); + // 获取回复消息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// System.out.println("ini-getNews-responseData: "+responseData.getResponseJsonDataString()); + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if (!dataString.equals("")) { + JSONArray dataJsonArray = new JSONArray(dataString); + String newsCacheString; + JSONObject newsCacheJsonObject; + Integer typeCodeInteger; + String contentString; + // 遍历每个新消息或者通知或者审批,将其更新到前端内存数据中 + for (int i = 0; i < dataJsonArray.length(); i++) { + newsCacheString = dataJsonArray.getString(i); + newsCacheJsonObject = new JSONObject(newsCacheString); + typeCodeInteger = newsCacheJsonObject.getInt("typeCode"); + contentString = newsCacheJsonObject.getString("content"); + if (typeCodeInteger == 0) { + // 处理新消息 + handleNewSingleMessage(contentString); + } else if (typeCodeInteger == 1) { + // 处理新通知 + } else if (typeCodeInteger == 2) { + // 处理新审批 + } + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + // 获取是否有新消息产生的json字符串 + private String getGetNewsJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getNews"; + String userNameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("uuid", uuidString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 处理一条接收到的新消息 + private void handleNewSingleMessage(String singleMessageString) { + try { +// System.out.println("handleNewSingleMessage start"); + JSONObject singleMessageJsonObject = new JSONObject(singleMessageString); + String fromString; + JSONObject fromJsonObject; + String fromNameString; + Integer idInteger; + String msgString; + Long timeLong; + // 解析发送方姓名 + fromString = singleMessageJsonObject.getString("from"); + fromJsonObject = new JSONObject(fromString); + fromNameString = fromJsonObject.getString("username"); + // 解析id + idInteger = singleMessageJsonObject.getInt("id"); + // 解析内容 + msgString = singleMessageJsonObject.getString("msg"); + // 解析时间 + timeLong = singleMessageJsonObject.getLong("time"); + // 构建一条消息对象 + SingleMessage singleMessage = new SingleMessage(); + singleMessage.setFromName(fromNameString); + singleMessage.setMsg(msgString); + singleMessage.setTime(timeLong); + singleMessage.setToName(mainApp.loginer.getUserName()); + singleMessage.setMsgId(idInteger); + // 加入登录未读消息列表中 + mainApp.iniNotReadMessageListWhenLogin.addNotReadMessage(singleMessage); + ; + System.out.println("Login-iniNotReadMessageListWhenLogin addSingleMessage success: " + singleMessageString); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 填充登录者与传入的联系人的消息记录[填充当前时间节点之前的五条消息] + private void fullMessageLogByPerson(Person person) { + if (person != null) { + // 发送请求,更新与该用户的消息记录 + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetRTMsgLogJson(person)); + mainApp.minoaDataAPI.executeOrder("/getRTMsgLog", orderInfo); + // 获取用户列表信息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// System.out.println(responseData.getResponseJsonDataString()); + JSONObject jsonObject = responseData.praseLoginData(); +// System.out.println("getRTMsgLog-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + // 赋值新的消息列表 + String dataString; + try { + dataString = jsonObject.getString("data"); +// System.out.println("getRTMsgLog-data: "+dataString); + if (dataString.equals("null") || dataString.equals("")) { + // 没有消息 + } else { + JSONArray dataJsonArray = new JSONArray(dataString); + String singleMessageCacheString; + // 遍历每个用户联系人,将其消息记录加入mainApp的消息列表中【倒序是为了让时间顺序】 + for (int i = dataJsonArray.length() - 1; i >= 0; i--) { + singleMessageCacheString = dataJsonArray.getString(i); +// System.out.println("singleMessage-"+i+": "+singleMessageCacheString); + praseAndAddSigleMessage(singleMessageCacheString, person); + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { + // 用户未登录,提示重新登录 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("登录信息过期"); + alert.setHeaderText("登录信息过期"); + alert.setContentText("你的登录信息过期,请重新登录。"); + alert.showAndWait(); + } + System.out.println("ini message log with " + person.getUserName() + " success"); + } + } + + // 解析单条消息记录并将其加入与特定联系人的消息记录里 + private void praseAndAddSigleMessage(String singleMessageString, Person person) { + try { + JSONObject singleMessageJsonObject = new JSONObject(singleMessageString); + // formName + String fromString = singleMessageJsonObject.getString("from"); + JSONObject fromJsonObject = new JSONObject(fromString); + String fromNameString = fromJsonObject.getString("username"); + // toName + String toString = singleMessageJsonObject.getString("to"); + JSONObject toJsonObject = new JSONObject(toString); + String toNameString = toJsonObject.getString("username"); + // time + Long timeLong = singleMessageJsonObject.getLong("time"); + // msg + String msgString = singleMessageJsonObject.getString("msg"); + // id + Integer idInteger = singleMessageJsonObject.getInt("id"); + // 构造类 + SingleMessage singleMessage = new SingleMessage(); + singleMessage.setFromName(fromNameString); + singleMessage.setToName(toNameString); + singleMessage.setTime(timeLong); + singleMessage.setMsg(msgString); + singleMessage.setMsgId(idInteger); + // 加入列表 + mainApp.messageLogMap.get(person.getUserName()).add(singleMessage); +// System.out.println(person.getUserName()+"的消息记录数目是:"+mainApp.messageLogMap.get(person.getUserName()).size()); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 获取精确到秒的时间戳 + private static Long getSecondTimestampTwo(Date date) { + if (null == date) { + return (long) 0; + } + String timestamp = String.valueOf(date.getTime() / 1000); + return Long.valueOf(timestamp); + } + + // 创建一个代表2019年9月1号的时间戳【精确到秒】 + public static Long get20190901() throws ParseException { + SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd"); + Date testData = dataFormat.parse("2019-09-01"); + return getSecondTimestampTwo(testData); + } + + // 创建一个当前时间的时间戳【精确到秒】 + public static Long getNowTimeStamp() { + Date date = new Date(); + return getSecondTimestampTwo(date); + } + + // "获取当前时间节点之前与该选中人的5条互发消息"[数据库给的数据是按照时间倒序的] + private String getGetRTMsgLogJson(Person selectedPerson) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getRTMsgLog"; + String userNameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String peerString = selectedPerson.getUserName(); + Long timestampLong = null; + timestampLong = getNowTimeStamp(); + // 期望获取的信息条数 + Integer limitInteger = 5; + // 0表示时间节点之后,1表示之前 + Integer directInteger = 1; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("peer", peerString); + jsonObject.put("timestamp", timestampLong); + jsonObject.put("limit", limitInteger); + jsonObject.put("direct", directInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + +// //"获取2019年9月1日之后与该选中人的100条互发消息" +// private String getGetRTMsgLogJson(Person selectedPerson) { +// JSONObject jsonObject =new JSONObject(); +// String commandString="/getRTMsgLog"; +// String userNameString=mainApp.loginer.getUserName(); +// String uuidString=mainApp.loginer.getUuid(); +// String peerString=selectedPerson.getUserName(); +// Long timestampLong = null; +// try { +// timestampLong=get20190901(); +// } catch (ParseException e1) { +// // TODO Auto-generated catch block +// e1.printStackTrace(); +// } +// //期望获取的信息条数 +// Integer limitInteger=100; +// //0表示时间节点之后,1表示之前 +// Integer directInteger=0; +// try { +// jsonObject.put("command", commandString); +// jsonObject.put("username", userNameString); +// jsonObject.put("uuid", uuidString); +// jsonObject.put("peer", peerString); +// jsonObject.put("timestamp", timestampLong); +// jsonObject.put("limit", limitInteger); +// jsonObject.put("direct", directInteger); +// } catch (Exception e) { +// // TODO: handle exception +// System.out.println("exception: " + e.getMessage()); +// } +// return jsonObject.toString(); +// } + + //获取上传文件的json格式 + public String getUploadFile(String filename, Integer offset) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/uploadFile"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + Integer offsetInteger = offset; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("offset", offsetInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + // 调试输出 + System.out.println("getUploadFile: " + jsonObject.toString()); + return jsonObject.toString(); + } +// +// /* +// * .文件上传操作 +// * @param serverpath 将该文件上传到的指定服务器路径 +// * @param file 要上传的本地文件 +// * @return 返回0表示传输成功;返回-1表示传输失败 +// */ +// public Integer sendFileToServer(File file, String serverpath) { +// //获取要上传文件的文件长度,计算上传次数 +// long filelen = file.length(); +// int cycle = (int) (filelen / mainApp.sliceSize); +// if(filelen % mainApp.sliceSize != 0){ +// cycle++; +// } +//// long filelen = file.length(); +//// int cycle = (int) filelen / mainApp.sliceSize; +//// int remainder=(int)filelen%mainApp.sliceSize; +//// if((cycle>0)&&(remainder==0)) { +//// cycle--; +//// } +// System.out.println("file-len: " + filelen); +// System.out.println("cycle: " + cycle); +// //发送上传命令给上传接口,接口自行取本地数据 +// int flagSuccess = 0; //记录收到200回复的次数 +// // 单线程上传 +//// for (int i = 0; i <= cycle; i++) { +//// OrderInfo orderInfo = new OrderInfo(); +//// ResponseData responseData = new ResponseData(); +//// Long seqLong = mainApp.getNewDataReqId(); +//// orderInfo.setSeq(seqLong); +//// //此时传入的文件路径是windows下的绝对路径 +//// orderInfo.setJsonString(getUploadFile(file.getAbsolutePath(), i * mainApp.sliceSize)); +//// +//// mainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo, serverpath); +//// responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +////// System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +//// JSONObject jsonObject = responseData.praseRequestData(); +//// responseData.printSelf(); +//// // 解析数据 +//// Integer codeInteger = null; +//// try { +//// codeInteger = jsonObject.getInt("code"); +//// } catch (JSONException e) { +//// // TODO Auto-generated catch block +//// e.printStackTrace(); +//// } +//// if (codeInteger == 200) { +//// flagSuccess++; +//// } +//// } +// // 多线程上传 +// if(filelen <= mainApp.sliceSize){ +// System.out.println("只有一个分片,不需要多线程上传"); +// OrderInfo orderInfo = new OrderInfo(); +// ResponseData responseData = new ResponseData(); +// Long seqLong = mainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// //此时传入的文件路径是windows下的绝对路径 +//// String testStr = getUploadFile(file.getAbsolutePath(), i * mainApp.sliceSize); +// String testStr = getUploadFile(file.getAbsolutePath(), 0); +// System.out.println("getUploadFileJson: " + testStr); +// orderInfo.setJsonString(testStr); +// mainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo, serverpath); +// responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// // System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +// JSONObject jsonObject = responseData.praseRequestData(); +//// responseData.printSelf(); +// // 解析数据 +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// flagSuccess++; +// } +// }else{ +// //多个分片,需要多线程上传 +// System.out.println("线程数为:"+mainApp.threadNumberForFileTransfer); +// UploadFileManager uploadFileManager = new UploadFileManager(file.getAbsolutePath(), +// serverpath, mainApp.threadNumberForFileTransfer, mainApp.sliceSize, filelen); +// flagSuccess = uploadFileManager.start(); +// } +// //每个分片都上传成功 +// if (flagSuccess == cycle) { +// System.out.println("已成功将文件" + file.getAbsolutePath() + "上传至服务器" + serverpath + "目录下。"); +//// sendTransSuccess(file,serverpath); +// return 0; +// } else { +// System.out.println("由于未知原因,本地文件" + file.getAbsolutePath() + "上传服务器失败。"); +// return -1; +// } +// } +// +// // 所有数据上传成功后发送一个offset=-1的数据请求 +// public void sendTransSuccess(File file,String serverpath) { +// OrderInfo orderInfo = new OrderInfo(); +// ResponseData responseData = new ResponseData(); +// Long seqLong = mainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// //此时传入的文件路径是windows下的绝对路径 +// orderInfo.setJsonString(getUploadFile(file.getAbsolutePath(), -1)); +// +// mainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo, serverpath); +// responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +//// System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +// JSONObject jsonObject = responseData.praseRequestData(); +// responseData.printSelf(); +// // 解析数据 +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// System.out.println("************成功发送文件传输成功的信号量**************"); +// } +// } +} diff --git a/src/main/java/cn/minoa/view/login/NDNBlockChainConnectClient.java b/src/main/java/cn/minoa/view/login/NDNBlockChainConnectClient.java new file mode 100644 index 0000000..f6a0aa1 --- /dev/null +++ b/src/main/java/cn/minoa/view/login/NDNBlockChainConnectClient.java @@ -0,0 +1,412 @@ +package cn.minoa.view.login; +// 与区块链建立TCP连接,发送注册请求 + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.KeyManager; +import net.named_data.jndn.Name; +import net.named_data.jndn.Signature; +import net.named_data.jndn.encoding.der.DerDecodingException; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.SigningInfo; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.v2.CertificateV2; +import net.named_data.jndn.util.Blob; +import net.named_data.jndn.util.Common; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.*; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.security.cert.Certificate; + +public class NDNBlockChainConnectClient { + + private String NDNBlockChainIPAddress; + private Integer NDNBlockChainPortAddress; + + public NDNBlockChainConnectClient(){ + this.NDNBlockChainIPAddress= MainApp.NDNBlockChainIPAddress; + System.out.println("区块链IP:"+MainApp.NDNBlockChainIPAddress); + this.NDNBlockChainPortAddress=MainApp.NDNBlockChainPortAddress; + System.out.println("区块链Port:"+MainApp.NDNBlockChainPortAddress); + } + + public NDNBlockChainConnectClient(String ipAddress,Integer portAddress){ + this.NDNBlockChainIPAddress= ipAddress; + this.NDNBlockChainPortAddress=portAddress; + } + + // 将已在区块链后台注册成功的用户注销掉 + public String deleteUser(String username,String sig){ + String prefix="/"+username; + // 获取注册数组 + byte[] registerByte=getBlockChainDeleteBuffer(prefix,sig); + System.out.println("delete length: "+registerByte.length); + // 通过TCP连接区块链后台 + Socket blockchainClient; + try { + blockchainClient=new Socket(NDNBlockChainIPAddress,NDNBlockChainPortAddress); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("连接不可达:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } + System.out.println("已成功建立TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress + +"; 远程主机地址:"+blockchainClient.getRemoteSocketAddress()); + try { + OutputStream outToServer = blockchainClient.getOutputStream(); + DataOutputStream out = new DataOutputStream(outToServer); + + out.write(registerByte); + InputStream inFromServer = blockchainClient.getInputStream(); + DataInputStream in = new DataInputStream(inFromServer); + System.out.println("正在等待服务器响应..."); + byte[] res = new byte[4]; + in.read(res); + int resSize = Bytes2Int_LE(res); + System.out.println("返回值长度为:"+ resSize); + res = new byte[resSize]; + in.read(res); + System.out.println("返回值内容为:" + new String(res, StandardCharsets.UTF_8)); + System.out.println("服务器响应: " ); + blockchainClient.close(); + System.out.println("已关闭此次TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); +// System.out.println("服务器响应: " ); + // 解析返回来的数据,如果是200,就返回true + try { + String resString=new String(res,StandardCharsets.UTF_8); + System.out.println("resString: "+resString); + JSONObject resObject=new JSONObject(resString); + int resCode=resObject.getInt("StatusCode"); + String message=resObject.getString("Message"); + if(resCode==200){ + System.out.println("区块链销户成功:"+username); + return "true"; + }else{ + return message; + } + } catch (JSONException e) { + e.printStackTrace(); + return e.getMessage(); + } + } catch (IOException e) { + e.printStackTrace(); + System.out.println("销户时数据交换出错:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } + } + + // 添加白名单 + public String addWhiteUser(String phone){ + // 获取注册数组 + byte[] registerByte=getBlockChainWhiteBuffer(phone); + System.out.println("whiteuser length: "+registerByte.length); + // 通过TCP连接区块链后台 + Socket blockchainClient; + try { + blockchainClient=new Socket(NDNBlockChainIPAddress,NDNBlockChainPortAddress); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("连接不可达:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } + System.out.println("已成功建立TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress + +"; 远程主机地址:"+blockchainClient.getRemoteSocketAddress()); + try { + OutputStream outToServer = blockchainClient.getOutputStream(); + DataOutputStream out = new DataOutputStream(outToServer); + + out.write(registerByte); + InputStream inFromServer = blockchainClient.getInputStream(); + DataInputStream in = new DataInputStream(inFromServer); + System.out.println("正在等待服务器响应..."); + byte[] res = new byte[4]; + in.read(res); + int resSize = Bytes2Int_LE(res); + System.out.println("返回值长度为:"+ resSize); + res = new byte[resSize]; + in.read(res); + System.out.println("返回值内容为:" + new String(res)); + blockchainClient.close(); + System.out.println("已关闭此次TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); +// System.out.println("服务器响应: " ); + // 解析返回来的数据,如果是200,就返回true + try { + String resString=new String(res); + System.out.println("resString: "+resString); + JSONObject resObject=new JSONObject(resString); + int resCode=resObject.getInt("StatusCode"); + String message=resObject.getString("Message"); + if(resCode==200){ + System.out.println("区块链添加白名单成功:"+phone); + return "true"; + }else{ + return message; + } + } catch (JSONException e) { + e.printStackTrace(); + return e.getMessage(); + } + } catch (IOException e) { + e.printStackTrace(); + System.out.println("添加白名单时数据交换出错:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } +// return true; + } + + // 注册用户 + public String registeNewUser(String username,String phone,String pubkey){ + // 获取注册数组 + byte[] registerByte=getBlockChainRegisterBuffer(username,phone, pubkey); + System.out.println("register length: "+registerByte.length); + // 通过TCP连接区块链后台 + Socket blockchainClient; + try { + blockchainClient=new Socket(NDNBlockChainIPAddress,NDNBlockChainPortAddress); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("连接不可达:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } + System.out.println("已成功建立TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress + +"; 远程主机地址:"+blockchainClient.getRemoteSocketAddress()); + try { + OutputStream outToServer = blockchainClient.getOutputStream(); + DataOutputStream out = new DataOutputStream(outToServer); + + out.write(registerByte); + InputStream inFromServer = blockchainClient.getInputStream(); + DataInputStream in = new DataInputStream(inFromServer); + System.out.println("正在等待服务器响应..."); + byte[] res = new byte[4]; + in.read(res); + int resSize = Bytes2Int_LE(res); + System.out.println("返回值长度为:"+ resSize); + res = new byte[resSize]; + in.read(res); + System.out.println("返回值内容为:" + new String(res, StandardCharsets.UTF_8)); + System.out.println("服务器响应: " ); + blockchainClient.close(); + System.out.println("已关闭此次TCP连接:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); +// System.out.println("服务器响应: " ); + // 解析返回来的数据,如果是200,就返回true + try { + String resString=new String(res,StandardCharsets.UTF_8); + System.out.println("resString: "+resString); + JSONObject resObject=new JSONObject(resString); + int resCode=resObject.getInt("StatusCode"); + String message=resObject.getString("Message"); + if(resCode==200){ + System.out.println("区块链注册用户成功:"+username); + return "true"; + }else{ + return message; + } + } catch (JSONException e) { + e.printStackTrace(); + return e.getMessage(); + } + } catch (IOException e) { + e.printStackTrace(); + System.out.println("注册时数据交换出错:"+NDNBlockChainIPAddress+":"+NDNBlockChainPortAddress); + return e.getMessage(); + } + } + + + private byte[] getBlockChainRegisterBuffer(String username,String phone, String pubkey) { + String registerString=getBlockChainRegisterJsonString(username,phone, pubkey); + System.out.println("json: "+registerString); + int jsonLen = registerString.getBytes(StandardCharsets.UTF_8).length; + System.out.println("json len: "+jsonLen); + byte[] registerByte=new byte[jsonLen+4]; +// System.arraycopy(intToBytesLittle(jsonLen),0, +// registerByte,0,4); + System.arraycopy(intToBytesLittle(jsonLen),0, + registerByte,0,4); + System.arraycopy(registerString.getBytes(StandardCharsets.UTF_8), + 0,registerByte,4,jsonLen); + return registerByte; + } + + private byte[] getBlockChainWhiteBuffer(String phone) { + String registerString=getBlockChainWhiteUserJsonString(phone); + System.out.println("json: "+registerString); + int jsonLen = registerString.getBytes().length; + System.out.println("json len: "+jsonLen); + byte[] registerByte=new byte[jsonLen+4]; + System.arraycopy(intToBytesLittle(jsonLen),0, + registerByte,0,4); + System.arraycopy(registerString.getBytes(StandardCharsets.UTF_8), + 0,registerByte,4,jsonLen); + return registerByte; + } + + private byte[] getBlockChainDeleteBuffer(String prefix,String sig) { + String registerString=getBlockChainDeleteUserJsonString(prefix,sig); + System.out.println("json: "+registerString); + int jsonLen = registerString.getBytes().length; + System.out.println("json len: "+jsonLen); + byte[] registerByte=new byte[jsonLen+4]; + System.arraycopy(intToBytesLittle(jsonLen),0, + registerByte,0,4); + System.arraycopy(registerString.getBytes(StandardCharsets.UTF_8), + 0,registerByte,4,jsonLen); + return registerByte; + } + + /** + * 以小端模式将int转成byte[] + * + * @param value + * @return + */ + public static byte[] intToBytesLittle(int value) { + byte[] src = new byte[4]; + src[3] = (byte) ((value >> 24) & 0xFF); + src[2] = (byte) ((value >> 16) & 0xFF); + src[1] = (byte) ((value >> 8) & 0xFF); + src[0] = (byte) (value & 0xFF); + return src; + } + + /** + * 以大端模式将int转成byte[] + * + * @param value + * @return + */ + public static byte[] intToBytesBig(int value) { + byte[] src = new byte[4]; + src[0] = (byte) ((value >> 24) & 0xFF); + src[1] = (byte) ((value >> 16) & 0xFF); + src[2] = (byte) ((value >> 8) & 0xFF); + src[3] = (byte) (value & 0xFF); + return src; + } + + /** + * 转换byte数组为int(小端) + * @return + * @note 数组长度至少为4,按小端方式转换,即传入的bytes是小端的,按这个规律组织成int + */ + public int Bytes2Int_LE(byte[] bytes){ + if(bytes.length < 4) + return -1; + int iRst = (bytes[0] & 0xFF); + iRst |= (bytes[1] & 0xFF) << 8; + iRst |= (bytes[2] & 0xFF) << 16; + iRst |= (bytes[3] & 0xFF)<< 24; + + return iRst; + } + + private String getBlockChainDeleteUserJsonString(String prefix,String sig) { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("Type", "user-act"); + jsonObject.put("Command", "Destroy"); + jsonObject.put("Prefix",prefix); + jsonObject.put("Timestamp",""); + jsonObject.put("Sig",sig); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + public static String signBuf(String prefix, CertificateV2 certificateV2){ + // 构建msg + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("Type", "user-act"); + jsonObject.put("Command", "Destroy"); + jsonObject.put("Prefix",prefix); + jsonObject.put("Timestamp",""); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + String msg=jsonObject.toString(); + ByteBuffer byteBuffer=ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8));// 这句是C++没有的 + try { + SigningInfo signingInfo = new SigningInfo(); + signingInfo.setSigningIdentity(certificateV2.getIdentity()); + Blob blob= KeyManager.INSTANCE.getKeyChain().sign(byteBuffer,signingInfo); +// String sig=blob.toHex(); + String sig=Common.base64Encode(blob.getImmutableArray(), false); +// String sig= Common.base64Encode(signature.getSignature().getImmutableArray()); +// String sig1=signature.getSignature().toHex(); + System.out.println("sig: "+sig); + return sig; + } catch (PibImpl.Error | KeyChain.Error | TpmBackEnd.Error e) { + e.printStackTrace(); + return ""; + } + } + + private String getBlockChainWhiteUserJsonString(String phone) { + JSONObject jsonObject = new JSONObject(); + String typeString = "whiteUser"; + String commandString = "addWhiteUser"; + String RealNameString = "王锋"; + Integer GenderInteger=0; + String IDCardString=""; + try { + jsonObject.put("Type", typeString); + jsonObject.put("Command", commandString); + jsonObject.put("RealName",RealNameString); + jsonObject.put("Phone",phone); + jsonObject.put("Gender",GenderInteger); + jsonObject.put("IDCard",IDCardString); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 从前端界面获得登录数据并转换成json格式 + private String getBlockChainRegisterJsonString(String username,String phone, String pubkey) { + JSONObject jsonObject = new JSONObject(); + String typeString = "user-act"; + String commandString = "Registry"; + String pubkeyString = pubkey; + String prefixString="/"+username; + Integer level=0; + String timestamp=""; + String IDCard=""; + String realname=""; + String aboutMe=""; + String face=""; + String print=""; + String iris=""; + String other=""; + String sig=""; + try { + jsonObject.put("Type", typeString); + jsonObject.put("Command", commandString); + jsonObject.put("Pubkey", pubkeyString); + jsonObject.put("Prefix",prefixString); + jsonObject.put("Level",level); + jsonObject.put("Timestamp",timestamp); + jsonObject.put("Username",username); + jsonObject.put("Realname",realname); + jsonObject.put("Phone",phone); + jsonObject.put("IDcard",IDCard); + jsonObject.put("AboutMe",aboutMe); + jsonObject.put("Face",face); + jsonObject.put("Print",print); + jsonObject.put("Iris",iris); + jsonObject.put("Other",other); + jsonObject.put("Sig",sig); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} diff --git a/src/main/java/cn/minoa/view/login/RegisterOverviewController.java b/src/main/java/cn/minoa/view/login/RegisterOverviewController.java new file mode 100644 index 0000000..354e215 --- /dev/null +++ b/src/main/java/cn/minoa/view/login/RegisterOverviewController.java @@ -0,0 +1,418 @@ +package cn.minoa.view.login; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.KeyManager; +import cn.minoa.dataRequestInterface.MinoaDataAPI; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.util.EncryptionUtil; +import cn.minoa.util.KeyStorageUtil; +import cn.minoa.util.RegisterUtil; +import cn.minoa.util.StringByteLengthUtil; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.stage.Stage; +import net.named_data.jndn.Interest; +import net.named_data.jndn.Name; +import net.named_data.jndn.Signature; +import net.named_data.jndn.security.KeyChain; +import net.named_data.jndn.security.SecurityException; +import net.named_data.jndn.security.SigningInfo; +import net.named_data.jndn.security.pib.Pib; +import net.named_data.jndn.security.pib.PibImpl; +import net.named_data.jndn.security.tpm.Tpm; +import net.named_data.jndn.security.tpm.TpmBackEnd; +import net.named_data.jndn.security.v2.CertificateV2; +import net.named_data.jndn.util.Common; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.nio.ByteBuffer; + +public class RegisterOverviewController { + + @SuppressWarnings("unused") + private MainApp mainApp; + @SuppressWarnings("unused") + private Stage dialogStage; + + @FXML + private TextField nickname; + @FXML + private PasswordField passwordField1; + @FXML + private PasswordField passwordField2; + @FXML + private TextField phoneNumber; + @FXML + private TextField verifyCode; + @FXML + private ImageView miniLogo; + + public RegisterOverviewController() { + } + + @FXML + private void initialize() { +// 在fxml被载入时候被自动调用 + miniLogo.setImage(new Image("file:resources/images/logo_mini.png")); + nickname.setPromptText("请输入一个系统新昵称..."); + phoneNumber.setPromptText("请输入白名单中的手机号..."); + verifyCode.setPromptText("预留功能,不需要输入。"); + verifyCode.setDisable(true); + } + + // 提交表单 + @FXML + private void handleRegister() { + if (isRightContent()) { + // 先拿到用户名,并修改keyChainNameString + String username=nickname.getText().trim(); + String keyChainNameString="/"+username; + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + // 生成本地pid.db文件,获取公钥 + String publicKey ; + try { + CertificateV2 certificateV2= KeyManager.INSTANCE.initKeyChain(keyChainNameString).getCertification(keyChainNameString); + publicKey = Common.base64Encode(certificateV2.wireEncode().getImmutableArray(), false); + System.out.println("本地密钥keyName:"+keyChainNameString); + System.out.println("publicKey: "+publicKey); + } catch (Pib.Error | PibImpl.Error | Tpm.Error | TpmBackEnd.Error | CertificateV2.Error | KeyChain.Error error) { + error.printStackTrace(); + // 弹窗报错 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("注册失败"); + alert.setHeaderText("注册账号失败"); + alert.setContentText("本地密钥生成失败:\n"+error.getMessage()+"\n" + +"建议更换用户名,重新进行注册!"); + alert.showAndWait(); + return ; + } + // 标志该用户是否已经通过了区块链注册 + boolean b_flag=RegisterUtil.isAccessedBlockUser(keyChainNameString); + // 如果没有注册成功过,向区块链发送注册信息;否则,跳过区块链注册 + if(!b_flag) { + String pubkey = publicKey; + String phonenumber = phoneNumber.getText().trim(); + NDNBlockChainConnectClient ndnBlockChainConnectClient = new NDNBlockChainConnectClient(); + String resString = ndnBlockChainConnectClient.registeNewUser(username, + phonenumber, pubkey); + if (resString.equals("true")) { + System.out.println("用户【" + username + "】在区块链后台被成功注册"); + } else { + // 区块链没有注册用户成功,则删掉刚刚生成的密钥文件 + try { + KeyManager.INSTANCE.getKeyChain().deleteIdentity(new Name(keyChainNameString)); + System.out.println("已删除本地密钥:"+keyChainNameString); + } catch (SecurityException e) { + e.printStackTrace(); + } + System.out.println("区块链后台注册用户【" + username + "】失败: " + resString); + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("区块链注册失败"); + alert.setHeaderText("区块链注册失败"); + alert.setContentText("失败原因:\n" + resString+"\n" + +"请确保你输入的手机号在系统白名单中,且用户名并没有被注册过。\n " + + "您可以将用户名适当复杂化,以保证其在系统中的唯一性。"); + alert.showAndWait(); + return; + } + } + // 获取注册时JSON + String registerJson=getRegisterJson(publicKey); + orderInfo.setJsonString(registerJson); + System.out.println("getRegisterJson: " + orderInfo.getJsonString()); + // 向系统后台发送注册信息 + mainApp.minoaDataAPI.executeOrder("/register", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + // 注册之后,使用getNews消耗掉新通知 +// makeNoticesBeRead(); + // 如果该用户是原来注册中断的用户,移除其本地信息 + if(b_flag) { + RegisterUtil.deleteAccessedBlockUser(keyChainNameString); + } + // 将此时的keyChainName持久化到本地 + MinoaDataAPI.keyChainNameString=keyChainNameString; + KeyStorageUtil.setKeyName(keyChainNameString); +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("注册成功"); + alert.setHeaderText("注册发送成功"); + alert.setContentText("你已成功注册一个账号。"); + alert.showAndWait(); + dialogStage.close(); + } else { + // 后台没有注册用户成功,则删掉刚刚生成的密钥文件 + try { + KeyManager.INSTANCE.getKeyChain().deleteIdentity(new Name(keyChainNameString)); + System.out.println("已删除本地密钥:"+keyChainNameString); + } catch (SecurityException e) { + e.printStackTrace(); + } + // 将该用户(区块链注册成功,后台注册失败)的注册前缀,保存到本地 + RegisterUtil.setAccessedBlockUser(keyChainNameString); +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("注册失败"); + alert.setHeaderText("注册失败"); + alert.setContentText("请确保你输入的手机号在系统白名单中,且用户名并没有被注册过。\n " + + "您可以将用户名适当复杂化,以保证其在系统中的唯一性。"); + alert.showAndWait(); + } + } + } + + // 将注册用户成功之前的所有通知状态修改为已阅 + //【目的是减少注册后第一次登录时由于getNews到通知而反复请求通知数据带来的卡顿】 + //【后台机制不是使用任何接口拿到通知都会变成已阅,而只能getNews可以这样,所以出此下策】 + // 突然发现没有拿到总通知数目的数据接口,所以先用getNews修改50条【上限】通知状态吧 + public void makeNoticesBeRead() { + //先登录,再修改通知的阅读状态 + boolean flag = fakeLogin(getLoginJsonString()); + if (flag) { + for(int i=0;i<10;i++){ + if(getNewsButDoNothing()){ + System.out.println("已成功修改"+(i+1)+"个数据片大小的通知的阅读状态。用户:"+mainApp.loginer.getUserName()); + } + } + } else { + System.out.println("假登录失败,通知状态未被修改为已阅。"); + } + } + + // 从前端界面获得登录数据并转换成json格式 + private String getLoginJsonString() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/login"; + String userNameString = nickname.getText().trim(); + String passWordString = passwordField1.getText().trim(); + mainApp.loginer.setUserName(userNameString); + mainApp.loginer.setPassWord(passWordString); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + private boolean fakeLogin(String loginJsonString) { + System.out.println("beginning fake login ..."); + boolean flag = false; + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(loginJsonString); + mainApp.minoaDataAPI.executeOrder("/login", orderInfo); + // 根据结果判断是否登录成功 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// responseData.printSelf(); + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + System.out.println("fake login 200"); + flag = true; + updateLoginer(jsonObject); + // 打印输出 + System.out.println("****注册后假登录【修改通知状态为已阅】****"); + mainApp.loginer.printSelf(); + System.out.println("****注册后假登录【修改通知状态为已阅】****"); + } + return flag; + } + + // 根据请求到的json数据更新loginer的信息 + private void updateLoginer(JSONObject loginerJsonObject) { + String dataString; + try { + dataString = loginerJsonObject.getString("data"); + JSONObject dataJsonObject = new JSONObject(dataString); + String uuid = dataJsonObject.getString("uuid"); + mainApp.loginer.setUuid(uuid); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 获取是否有新的[消息/通知/审批]产生的json字符串 + private String getGetNewsJson() { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getNews"; + String userNameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("uuid", uuidString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //发送getNews请求 + public boolean getNewsButDoNothing() { + // 发送数据请求 + Long seqLong = mainApp.getNewDataReqId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetNewsJson()); + mainApp.minoaDataAPI.executeOrder("/getNews", orderInfo); + // 获取回复消息 + ResponseData responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +// System.out.println("getNews-responseData: "+responseData.getResponseJsonDataString()); + JSONObject jsonObject = responseData.praseRequestData(); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + try { + if(!jsonObject.getString("data").equals("")){ + return true; + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return false; + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + /* + * @param dialogStage + */ + public void setDialogStage(Stage dialogStage) { + this.dialogStage = dialogStage; + } + + // 获取注册请求字符串 + private String getRegisterJson(String mPubKey) { + JSONObject jsonObject = new JSONObject(); +// String mPubKey = "Bv0CsQcuCAR3Z2gwCANLRVkICH9+4yjVUHZaCAxjZXJ0LXJlcXVlc3QICf0AAAFv\nEUSBehQJGAECGQQANu6AFf0BJjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBANbFutCAZqFjHEa16tYvy4lBgi9/zixKWQzE9I72vaG2Imym+6KR30qkAQOK\nh0uE5RD2YsGhRK99rde+JOqIfk2/sunhp4nctOS5f4DeuOty58ekrO+Co9ZFmNBI\nmyk5i/hakjBEd/vdD7b1VadDRep5mWs3rgCinZOUfVaZ4oKqSws7ZWMvOSlRMOOi\nVzj53P4poY109S6aWmi3xKw/D76vfJapjdOB9Dg7S3rz0Rve1F/ajHCXWLt4XU3k\nUCiBn3Qmt6UD/MyDBaaUCfOvHTN6yVPtN6aOtYJVBAWt/P1ahLYhWkSm6KvrwMt0\nRzy2lw/lPriT99REItc/0wK6XwkCAwEAARZGGwEBHBcHFQgEd2doMAgDS0VZCAh/\nfuMo1VB2Wv0A/Sb9AP4PMjAxOTEyMTdUMDAzMTQw/QD/DzIwMTkxMjI3VDAwMzEz\nORf9AQCM+8WROXfi8WD9DydfLuSUxV0v3KW/Gn422/puDrkwwi1R2ogsAUc2O8pp\npuMLjWrQOTgE6ZTg80uWoDSUjjk44aMjkL2SAfp62vHRbGFx7+dckHAtF+DS6NH7\nX2vojFHOg6fksOE1M7XqLHmEEctZrFVSnQO7nB3DfRg+BqzZC/iy+8YAvTIvqA1c\nNsKYkmNITB80aM1v2H7fKnV4ZJpZnW71G5vbaPUseeklalQ2vEuBgWdd/1PL/0Ml\nI52npzqoeKAEpLp+vBwGN5ryGtWKB12nNimpMfevQAn/9BVIg59lZ7DIlE0sWmgW\nnwDxIXKebdViOejHlKwNAykxRRWw"; + String commandString = "/register"; + String userNameString = nickname.getText().trim(); + String passWordString = passwordField1.getText().trim(); + // 对密码进行加密 + passWordString= EncryptionUtil.getMD5String(passWordString); + String phoneString = phoneNumber.getText().trim(); + Integer levelInteger = 0; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", userNameString); + jsonObject.put("password", passWordString); + jsonObject.put("phone", phoneString); + jsonObject.put("publicKey", mPubKey); + jsonObject.put("level", levelInteger); + } catch (Exception e) { + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 验证输入内容格式 + private boolean isRightContent() { + //验证昵称是否为空 + if (nickname.getText().trim().equals("")||nickname.getText().trim().contains("@")||nickname.getText().trim().contains(" ")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("账号错误"); + alert.setHeaderText("你的账号错误"); + alert.setContentText("请确保你输入的账号(昵称)不为空,且昵称中不包含\"@\"字符;\n" + + "此外,请确保输入的昵称中不包含空格"); + alert.showAndWait(); + return false; + } + //验证昵称是否为"default" + if (nickname.getText().trim().equals("default")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("账号错误"); + alert.setHeaderText("你的账号错误"); + alert.setContentText("default为系统保留账号(昵称),请更换一个昵称。"); + alert.showAndWait(); + return false; + } + //验证两次密码 + if (passwordField1.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("密码错误"); + alert.setHeaderText("你设置的密码错误"); + alert.setContentText("请确保你设置的密码不为空。"); + alert.showAndWait(); + return false; + } + if (!passwordField1.getText().trim().equals(passwordField2.getText().trim())) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("密码错误"); + alert.setHeaderText("你的设置的密码错误"); + alert.setContentText("请确保你输入的两次密码是一致的。"); + alert.showAndWait(); + return false; + } + if (phoneNumber.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("手机号错误"); + alert.setHeaderText("你的输入的手机号错误"); + alert.setContentText("请确保你输入的手机号不为空。"); + alert.showAndWait(); + return false; + } + if (!StringByteLengthUtil.isAllNumber(phoneNumber.getText().trim())) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("手机号错误"); + alert.setHeaderText("你的设置的手机号错误"); + alert.setContentText("请确保你输入的手机号的格式是正确的。"); + alert.showAndWait(); + return false; + } + return true; + } + +} diff --git a/src/main/java/cn/minoa/view/login/SettingOverviewController.java b/src/main/java/cn/minoa/view/login/SettingOverviewController.java new file mode 100644 index 0000000..bb0e34c --- /dev/null +++ b/src/main/java/cn/minoa/view/login/SettingOverviewController.java @@ -0,0 +1,410 @@ +package cn.minoa.view.login; + +import cn.minoa.MainApp; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.stage.Stage; + +import java.io.*; +import java.net.InetSocketAddress; +import java.net.Socket; +//import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.regex.Pattern; + +public class SettingOverviewController { + @SuppressWarnings("unused") + private MainApp mainApp; + @SuppressWarnings("unused") + private Stage dialogStage; + + @FXML + private TextField ipAddress; + @FXML + private TextField portAddress; + @FXML + private TextField packetPrefix; + @FXML + private TextField fileThread; + @FXML + private TextField BlockIP; + @FXML + private TextField BlockPort; + @FXML + private ImageView miniLogo; + + public SettingOverviewController(){ + } + + @FXML + private void initialize() { +// 在fxml被载入时候被自动调用,在setMainApp那里将修改为显示配置文件信息 + miniLogo.setImage(new Image("file:resources/images/logo_mini.png")); + ipAddress.setPromptText("IP格式示例:121.15.171.89"); + portAddress.setPromptText("端口号格式示例:6363"); + packetPrefix.setPromptText("格式示例:/ndn/edu/pkusz/OA)"); + fileThread.setPromptText("文件传输并发数[1-100]"); + BlockIP.setPromptText("IP格式示例:121.15.171.89"); + BlockPort.setPromptText("端口号格式示例:8010"); + } + + // 根据表单修改配置文件 + @FXML + private void handleSetting() { + if (isRightContent()) { + //修改本地配置文件 + changeSettingProperties(); + mainApp.loadSettingProperties(); + reloadSetting(); + //弹窗提示 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("设置成功"); + alert.setHeaderText("配置文件修改成功"); + alert.setContentText("您已成功修改系统配置。\n请重新启动应用以生效新设置!"); + alert.showAndWait(); + System.exit(0); + } + } + + private void changeSettingProperties(){ + InputStream inputStream = null; + OutputStream outputStream=null; + try { + //如果 classPath 不存在,则获取的输入流会为 null + inputStream=new BufferedInputStream(new FileInputStream(mainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); +// inputStream = MainApp.class.getClassLoader().getResourceAsStream("ndn/settings.properties"); + if (inputStream == null) { + System.out.println("没有找到配置文件..."); + } + // 解决中文乱码问题 + BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + Properties settingProperties = new Properties(); + settingProperties.load(bf); + settingProperties.setProperty("ServerIPAddress",ipAddress.getText().trim()); + settingProperties.setProperty("ServerPortAddress",portAddress.getText().trim()); + settingProperties.setProperty("DataPackagePrefix",packetPrefix.getText().trim()); + settingProperties.setProperty("ThreadNumberForFileTransfer",fileThread.getText().trim()); + settingProperties.setProperty("ndnBlockChainIPAddress",BlockIP.getText().trim()); + settingProperties.setProperty("ndnBlockChainPortAddress",BlockPort.getText().trim()); + + outputStream=new BufferedOutputStream(new FileOutputStream(mainApp.getClientPath()+ File.separator+"ndn"+File.separator+"settings.properties")); + // 解决中文乱码问题 + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)); +// URL url = MainApp.class.getClassLoader().getResource("ndn/settings.properties"); +// outputStream = new FileOutputStream(url.getPath()); + settingProperties.store(bw,"修改配置文件"); + } catch (IOException e) { + e.printStackTrace(); + }finally{ + //关闭输入流 + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + //关闭输出流 + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // 重载参数 + private void reloadSetting(){ + ipAddress.setText(mainApp.minoaServerIpString); + portAddress.setText(mainApp.minoaServerPortInteger.toString()); + fileThread.setText(mainApp.threadNumberForFileTransfer.toString()); + packetPrefix.setText(mainApp.globalPrefixString); + packetPrefix.setDisable(true); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + ipAddress.setText(mainApp.minoaServerIpString); + portAddress.setText(mainApp.minoaServerPortInteger.toString()); + fileThread.setText(mainApp.threadNumberForFileTransfer.toString()); + packetPrefix.setText(mainApp.globalPrefixString); + packetPrefix.setDisable(true); + BlockIP.setText(mainApp.NDNBlockChainIPAddress); + BlockPort.setText(mainApp.NDNBlockChainPortAddress.toString()); + } + + /* + * @param dialogStage + */ + public void setDialogStage(Stage dialogStage) { + this.dialogStage = dialogStage; + } + + // 验证输入内容格式 + private boolean isRightContent() { + //验证IP是否为空 + if (ipAddress.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("IP错误"); + alert.setHeaderText("你的IP错误"); + alert.setContentText("请确保你输入的IP不为空。"); + alert.showAndWait(); + return false; + } + //验证IP格式是否正确 + if (!ipCheck(ipAddress.getText().trim())) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("IP错误"); + alert.setHeaderText("你的IP错误"); + alert.setContentText("请确保你输入的IP格式正确。"); + alert.showAndWait(); + return false; + } + //验证端口号是否为空 + if (portAddress.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("端口号错误"); + alert.setHeaderText("你的端口号错误"); + alert.setContentText("请确保你输入的端口号不为空。"); + alert.showAndWait(); + return false; + } + //判断端口号是否正确 + boolean flag_port=true; + try{ + Integer port=Integer.parseInt(portAddress.getText().trim()); + if((port<1)||(port>65535)){ + flag_port=false; + } + }catch(Exception e){ + flag_port=false; + }finally { + if(flag_port==false){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("端口号错误"); + alert.setHeaderText("你的端口号错误"); + alert.setContentText("请确保你输入的端口号格式正确。"); + alert.showAndWait(); + return false; + } + } + //验证前缀是否为空 + if (packetPrefix.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("包前缀错误"); + alert.setHeaderText("你的包前缀错误"); + alert.setContentText("请确保你输入的包前缀不为空。"); + alert.showAndWait(); + return false; + } + //判断前缀格式是否正确 + if ((!packetPrefix.getText().trim().equals(""))&&(!prefixCheck(packetPrefix.getText().trim()))) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("包前缀错误"); + alert.setHeaderText("你的包前缀错误"); + alert.setContentText("请确保你输入的包前缀格式正确。"); + alert.showAndWait(); + return false; + } + //验证前缀是否为空 + if (fileThread.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("线程数错误"); + alert.setHeaderText("你的线程数错误"); + alert.setContentText("请确保你输入的线程数不为空。"); + alert.showAndWait(); + return false; + } + //判断线程数是否正确 + boolean flag_thread=true; + try{ + Integer threadnumber=Integer.parseInt(fileThread.getText().trim()); + if((threadnumber<1)||(threadnumber>100)){ + flag_thread=false; + } + }catch(Exception e){ + flag_thread=false; + }finally { + if(flag_thread==false){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("线程数错误"); + alert.setHeaderText("你的线程数错误"); + alert.setContentText("请确保你输入的文件传输线程数是正整数且范围在1-100之间。"); + alert.showAndWait(); + return false; + } + } + //使用socket判断这里的ip和端口号是否可以连接上 + String ipHost=ipAddress.getText().trim(); + Integer portHost=Integer.parseInt(portAddress.getText().trim()); + if(!isHostConnectable(ipHost,portHost)){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("连接错误"); + alert.setHeaderText("地址连接错误"); + alert.setContentText("请确保你输入的服务器IP及端口号是可达的。"); + alert.showAndWait(); + return false; + } + //验证IP是否为空 + if (BlockIP.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("IP错误"); + alert.setHeaderText("你的区块链IP错误"); + alert.setContentText("请确保你输入的区块链IP不为空。"); + alert.showAndWait(); + return false; + } + //验证IP格式是否正确 + if (!ipCheck(BlockIP.getText().trim())) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("IP错误"); + alert.setHeaderText("你的区块链IP错误"); + alert.setContentText("请确保你输入的区块链IP格式正确。"); + alert.showAndWait(); + return false; + } + //验证端口号是否为空 + if (BlockPort.getText().trim().equals("")) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("端口号错误"); + alert.setHeaderText("你的区块链端口号错误"); + alert.setContentText("请确保你输入的区块链端口号不为空。"); + alert.showAndWait(); + return false; + } + //判断端口号是否正确 + boolean flag_b_port=true; + try{ + Integer port=Integer.parseInt(BlockPort.getText().trim()); + if((port<1)||(port>65535)){ + flag_b_port=false; + } + }catch(Exception e){ + flag_b_port=false; + }finally { + if(flag_b_port==false){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("端口号错误"); + alert.setHeaderText("你的区块链端口号错误"); + alert.setContentText("请确保你输入的区块链端口号格式正确。"); + alert.showAndWait(); + return false; + } + } + //使用socket判断这里的ip和端口号是否可以连接上 + ipHost=BlockIP.getText().trim(); + portHost=Integer.parseInt(BlockPort.getText().trim()); + if(!isHostConnectable(ipHost,portHost)){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("连接错误"); + alert.setHeaderText("地址连接错误"); + alert.setContentText("请确保你输入的区块链IP及端口号是可达的。"); + alert.showAndWait(); + return false; + } + //判断是否与原内容一致 + if(isNotChange()){ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("设置未变"); + alert.setHeaderText("设置未变"); + alert.setContentText("请确保新配置信息与旧配置信息是不同的。"); + alert.showAndWait(); + return false; + } + return true; + } + + // 判断新设置的参数是否与原参数一致 + public boolean isNotChange(){ + if(ipAddress.getText().trim().equals(mainApp.minoaServerIpString)&& + portAddress.getText().trim().equals(mainApp.minoaServerPortInteger.toString())&& + packetPrefix.getText().trim().equals(mainApp.globalPrefixString)&& + fileThread.getText().trim().equals(mainApp.threadNumberForFileTransfer.toString())&& + BlockIP.getText().trim().equals(mainApp.NDNBlockChainIPAddress)&& + BlockPort.getText().trim().equals(mainApp.NDNBlockChainPortAddress.toString())){ + return true; + }else{ + return false; + } + } + + // 判断ip和端口号是否可以连接 + public static boolean isHostConnectable(String host, int port) { + Socket socket = new Socket(); + try { + socket.connect(new InetSocketAddress(host, port)); + } catch (IOException e) { +// e.printStackTrace(); + return false; + } finally { + try { + socket.close(); + } catch (IOException e) { + //do nothing + } + } + return true; + } + + //判断IP合法性【参考:https://blog.csdn.net/bokerr/article/details/83508415】 + public static boolean ipCheck(String ipAddressString) { + //标准IPv4地址的正则表达式: + Pattern IPV4_REGEX = + Pattern.compile( + "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); + //无全0块,标准IPv6地址的正则表达式 + Pattern IPV6_STD_REGEX = + Pattern.compile( + "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); + //压缩正则表达式 + Pattern IPV6_COMPRESS_REGEX = + Pattern.compile( + "^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$"); + + //判断是否为合法IPv4地址 + if(IPV4_REGEX.matcher(ipAddressString).matches()){ + return true; + }else if(IPV6_COMPRESS_REGEX.matcher(ipAddressString).matches()){ + return true; + }else if(IPV6_STD_REGEX.matcher(ipAddressString).matches()){ + return true; + }else{ + return false; + } + } + + //判断前缀格式是否正确 + public static boolean prefixCheck(String prefixString){ + if(prefixString.length()<2){ + return false; + } + if((prefixString.charAt(0)!='/')||(prefixString.charAt(prefixString.length()-1)=='/')){ + return false; + } + return true; + } +} diff --git a/src/main/java/cn/minoa/view/login/testRegister.java b/src/main/java/cn/minoa/view/login/testRegister.java new file mode 100644 index 0000000..4b19712 --- /dev/null +++ b/src/main/java/cn/minoa/view/login/testRegister.java @@ -0,0 +1,20 @@ +package cn.minoa.view.login; + +public class testRegister { + public static void main(String[] args){ +// String pubkey = "Bv0CIgcvCAVsbGFhYQgDS0VZCAhyVwiW+kaX7QgMY2VydC1yZXF1ZXN0CAn9AAABcSP8KDIUCRgBAhkEAiVRABX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAEgCbgPaAk0RRziBAW/beuF5fBQdJdbZhtKrT+FRdXq6sqPxvhM3VQF+WrBehiA5TsKpLzwzNoE8kKt8gyevM4dxZHGwEDHBgHFggFbGxhYWEIA0tFWQgIclcIlvpGl+39AP0m/QD+DzIwMjAwMzI5VDAxNTEwMf0A/w8yMDIwMDQwOFQwMTUxMDAXSDBGAiEApxH8lJntSBM/NwV+laSdifH674+76xZnxVF4vKGaXXkCIQCj3OyzagvjqYrJmZ9f00PWVISXRyrIrKmwFbwzsk/hQw=="; + NDNBlockChainConnectClient ndnBlockChainConnectClient=new NDNBlockChainConnectClient("116.77.74.139",9001); +// String res=ndnBlockChainConnectClient.registeNewUser("wf001", +// "003",pubkey); +// System.out.println("添加白名单结果:"+res); + for(Integer i=21;i<=30;i++){ + String res=ndnBlockChainConnectClient.addWhiteUser("3"+i); + System.out.println("添加白名单结果:"+res); + } +// String res_del=ndnBlockChainConnectClient.deleteUser("test_0416v2",""); +// if(res_del.equals("true")){ +// System.out.println("区块链销号成功"); +// } + + } +} \ No newline at end of file diff --git a/src/main/java/cn/minoa/view/message/ChatBoxController.java b/src/main/java/cn/minoa/view/message/ChatBoxController.java new file mode 100644 index 0000000..d4f2e18 --- /dev/null +++ b/src/main/java/cn/minoa/view/message/ChatBoxController.java @@ -0,0 +1,405 @@ +package cn.minoa.view.message; +// 聊天界面 + +import java.util.Date; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.SingleMessage; +import cn.minoa.util.Tool; +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.Pane; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; + +public class ChatBoxController { + private MainApp mainApp; + private String contactName; + private OrderInfo orderInfo; + + @FXML + private ImageView contactPhoto; + @FXML + private Label contactNameLabel; + @FXML + private TextArea sendMsgTextArea; + + @FXML + private ListView chatList; + + public ChatBoxController() { + // TODO Auto-generated constructor stub + orderInfo=new OrderInfo(); + } + + @FXML + private void initialize() { + //页面被加载时执行 + contactNameLabel.setText("佚名"); + //textarea设置提示信息 + sendMsgTextArea.setPromptText("请输入一段200字符以内的无换行消息..."); + //textarea接收回车键执行发送请求 + sendMsgTextArea.setOnKeyPressed(new EventHandler() { + @Override + public void handle(KeyEvent keyEvent) { + if (keyEvent.getCode() == KeyCode.ENTER) { + //如果接收到了回车键的事件,则触发发送消息函数 + handleSendMessage(); + } + } + }); + } + + public void setContactName(String personName) { + this.contactName=personName; + } + + public String getContactName() { + return this.contactName; + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + //被mainApp调用 + this.mainApp = mainApp; + contactNameLabel.setText(contactName); + contactPhoto.setImage(new Image(String.format("file:resources/images/headPhotos/%s.png",this.contactName))); + //初始化聊天记录 + iniMessageLog(); + } + + //根据内存数据初始化部分消息记录 + private void iniMessageLog() { + //获取与当前用户的消息记录表 + ObservableList messageLogByPersonCacheList; + messageLogByPersonCacheList=mainApp.messageLogMap.get(this.contactName); + for (SingleMessage singleMessage : messageLogByPersonCacheList) { + praseSingleMessageToMessagePane(singleMessage); + } + } + + //对于单条消息记录,根据发送方分配不同的消息气泡 + public void praseSingleMessageToMessagePane(SingleMessage singleMessage) { + if(singleMessage.getFromName().equals(mainApp.loginer.getUserName())) { + //如果是自己发的消息 +// addRight("[我]: \n"+singleMessage.getMsg()); +// addRight(mainApp.loginer.getUserName(),"我: \n"+singleMessage.getMsg()); + addRight(mainApp.loginer.getUserName(),singleMessage.getMsg()); + }else { +// addLeft("["+this.contactName+"]: \n"+singleMessage.getMsg()); +// addLeft(this.contactName,""+this.contactName+": \n"+singleMessage.getMsg()); + addLeft(this.contactName,singleMessage.getMsg()); + } + } + + //添加消息气泡:别人的消息 + public void addLeft(String head,String Msg){ + chatList.getItems().add(new ChatListItem().Left(head,Msg, Tool.getWidth(Msg),Tool.getHight(Msg))); + } + public void addLeft(String Msg){ + chatList.getItems().add(new ChatListItem().Left("",Msg, Tool.getWidth(Msg),Tool.getHight(Msg))); + } + + //添加消息气泡自己的消息 + public void addRight(String head,String Msg){ + chatList.getItems().add(new ChatListItem().Right(head,Msg,Tool.getWidth(Msg),Tool.getHight(Msg))); + } + public void addRight(String Msg){ + chatList.getItems().add(new ChatListItem().Right("",Msg,Tool.getWidth(Msg),Tool.getHight(Msg))); + } + + @FXML + private void handleSendMessage() { + //点击发送按钮时执行的动作 + String jsonString=getSendRTMsgJson(); + if(jsonString.equals("")) { + //发送的消息为空或空白字符串 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("消息内容错误"); + alert.setHeaderText("你的消息内容为空"); + alert.setContentText("请检查你要发送的消息内容,消息内容不能为空或空白字符串."); + alert.showAndWait(); + }else if(jsonString.equals("tooLong")){ + //发送的消息长度过长 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("消息内容错误"); + alert.setHeaderText("你的消息内容长度过长"); + alert.setContentText("请检查你要发送的消息内容,消息内容不能超过200个字符."); + alert.showAndWait(); + }else if(jsonString.equals("hasEnter")){ + //发送的消息为空或空白字符串 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("消息内容错误"); + alert.setHeaderText("你的消息内容中含有换行符"); + alert.setContentText("请检查你要发送的消息内容,消息内容中不能包含换行符."); + alert.showAndWait(); + }else{ + //发送数据请求 + Long seqLong=mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(jsonString); + mainApp.minoaDataAPI.executeOrder("/sendRTMsg", orderInfo); + //获取用户列表信息 + ResponseData responseData=mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject=responseData.praseLoginData(); + System.out.println("/sendRTMsg-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(codeInteger==200) { + //更新消息列表=>增加一条新的消息 + SingleMessage singleMessage=new SingleMessage(); + singleMessage.setFromName(mainApp.loginer.getUserName()); + singleMessage.setToName(this.contactName); + singleMessage.setTime(getNowSecondTimestamp()); + singleMessage.setMsg(sendMsgTextArea.getText().trim()); + //加入内存数据结构里 + mainApp.messageLogMap.get(this.contactName).add(singleMessage); + //产生消息气泡 + praseSingleMessageToMessagePane(singleMessage); + //消息发送成功,清空textarea + this.sendMsgTextArea.clear(); + //将chatList的垂直滑动条滑到底部 + //。。。 + }else { + //用户未登录,提示重新登录 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("登录信息过期"); + alert.setHeaderText("登录信息过期"); + alert.setContentText("你的登录信息过期,请重新登录。"); + alert.showAndWait(); + } + } + } + + //获取当前日期的时间戳[精确到秒] + private static Long getNowSecondTimestamp(){ + Date date=new Date(); + String timestamp = String.valueOf(date.getTime()/1000); + return Long.valueOf(timestamp); + } + + //获取发送消息时候的JSON字符串 + private String getSendRTMsgJson() { + String msgString=sendMsgTextArea.getText().trim(); + if(msgString.equals("")) { + //空白字符串 + return ""; + }else if(msgString.length()>200) { + //超长字符串 + return "tooLong"; + }else if(hasEnter(msgString)) { + //有换行符出现 + return "hasEnter"; + } + JSONObject jsonObject =new JSONObject(); + String commandString="/sendRTMsg"; + String fromString=mainApp.loginer.getUserName(); + String toString=this.contactName; + String uuidString=mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("from", fromString); + jsonObject.put("to", toString); + jsonObject.put("uuid", uuidString); + jsonObject.put("msg", msgString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + //判断一个字符串内有无换行符 + private boolean hasEnter(String string) { + boolean flag=false; + if((string.indexOf("\n")>=0)) { + flag=true; + } + return flag; + } + + //设置联系人头像=>冗余 + public void creatHead() { + Button head=new Button(); + head.getStyleClass().add("head"); + head.setPrefSize(40,40); + head.setLayoutY(5); + head.setLayoutX(8); + head.setStyle(String.format("-fx-background-image: url('file:resources/images/headPhotos/%s.png')",this.contactName)); + } + +} + +//聊天消息列表的一个列表项 +class ChatListItem { + //外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + //头像 + private Button head; + private Integer headLength; + //消息气泡文本区域 + private TextArea text; + private Integer textWidth; + private Integer textHeight; + //左pane + private Pane left; + private Integer insidePaneWidth; + private Integer insidePaneHeight; + //右pane + private Pane right; + //消息气泡旁的一个小箭头 + private Button arrow; + private Integer arrowLength; + + //pane的基础高度 + private Integer paneIniHeightInteger; + private Integer insidePaneIniHeightInteger; + + //组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + private Integer arrowXInteger; + private Integer arrowYInteger; + + //设置消息头像 + private void setHeadPortrait(Button button, String head){ +// button.setStyle(String.format("-fx-background-image: url('file:resources/images/mImage/test_headPhoto.png')")); + button.setStyle(String.format("-fx-background-image: url('file:resources/images/headPhotos/%s.png')",head)); + } + + //构造函数 + public ChatListItem(){ + //消息气泡大小配置 + paneWidth=450; + paneHeight=150; + arrowLength=32; + insidePaneWidth=350; + insidePaneHeight=70; + headLength=50; + textWidth=320; + textHeight=50; + //消息气泡位置配置 + headXInteger=10; + headYInteger=10; + textXInteger=100-20; + textYInteger=30; + arrowXInteger=85-20; + arrowYInteger=40; + //pane基础高度 + paneIniHeightInteger=110; + insidePaneIniHeightInteger=30; + + pane = new Pane(); + head = new Button(); + text = new TextArea(); + //最外面的pane的大小 + pane.setPrefSize(paneWidth,paneHeight); + left = new Pane(); + right = new Pane(); + arrow = new Button(); + arrow.setDisable(false); + arrow.setPrefSize(arrowLength,arrowLength); + left.setPrefSize(insidePaneWidth,insidePaneHeight); + right.setPrefSize(insidePaneWidth,insidePaneHeight); + //设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + left.getStyleClass().add("pane"); + right.getStyleClass().add("pane"); + //设置消息头像和消息文本属性 + head.setPrefSize(headLength,headLength); + text.setPrefSize(textWidth,textHeight); + text.setWrapText(true); + text.setEditable(false); + } + + //收到别人的消息 + public Pane Left(String ihead,String itext,double width,double hight){ + //设置CSS样式标识 + text.getStyleClass().add("lefttext"); + arrow.getStyleClass().add("leftarrow"); + //pane的高度设置 + pane.setPrefHeight(paneIniHeightInteger+hight); + left.setPrefHeight(insidePaneIniHeightInteger+hight); + //头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + //text的高宽以及位置 + text.setPrefSize(width,hight); + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + //箭头的位置 + arrow.setLayoutY(arrowYInteger); + arrow.setLayoutX(arrowXInteger); + //头像和气泡文本内容 + text.setText(itext); + setHeadPortrait(head,ihead); + //将头像、箭头、消息气泡加入左pane + left.getChildren().add(head); + left.getChildren().add(arrow); + left.getChildren().add(text); + //将左Pane加入pane + pane.getChildren().add(left); + return pane; + } + + //收到自己的消息 + public Pane Right(String ihead,String itext,double width,double hight){ + //设置CSS样式标识 + text.getStyleClass().add("righttext"); + arrow.getStyleClass().add("rightarrow"); + //pane的高度设置 + pane.setPrefHeight(paneIniHeightInteger+hight); + right.setPrefHeight(insidePaneIniHeightInteger+hight); + //头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(insidePaneWidth-headLength-headXInteger); + //text的高宽以及位置 + text.setPrefSize(width,hight); + text.setLayoutY(textYInteger); + text.setLayoutX(insidePaneWidth-textXInteger-width); + //箭头的位置 + arrow.setLayoutY(arrowYInteger); + arrow.setLayoutX(insidePaneWidth-arrowLength+10-arrowXInteger); + //头像和气泡文本内容 + text.setText(itext); + setHeadPortrait(head,ihead); + //将头像、箭头、消息气泡加入右pane + right.getChildren().add(head); + right.getChildren().add(arrow); + right.getChildren().add(text); + //设置右pane位置 + right.setLayoutX(paneWidth-insidePaneWidth); + //将右Pane加入pane + pane.getChildren().add(right); + return pane; + } +} diff --git a/src/main/java/cn/minoa/view/message/ChatBoxController_Version1.java b/src/main/java/cn/minoa/view/message/ChatBoxController_Version1.java new file mode 100644 index 0000000..6055183 --- /dev/null +++ b/src/main/java/cn/minoa/view/message/ChatBoxController_Version1.java @@ -0,0 +1,152 @@ +package cn.minoa.view.message; +// 聊天界面 + +import java.util.Date; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.SingleMessage; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.Alert.AlertType; + +public class ChatBoxController_Version1 { + private MainApp mainApp; + private String contactName; + private OrderInfo orderInfo; + + @FXML + private TableView messageLogTable; + @FXML + private TableColumn timeColumn; + @FXML + private TableColumn senderColumn; + @FXML + private TableColumn receiverColumn; + @FXML + private TableColumn msgColumn; + + @FXML + private Label contactNameLabel; + + @FXML + private TextArea sendMsgTextArea; + + public ChatBoxController_Version1() { + // TODO Auto-generated constructor stub + orderInfo=new OrderInfo(); + } + + public void setContactName(String personName) { + this.contactName=personName; + } + + @FXML + private void initialize() { + //页面被加载时执行 + contactNameLabel.setText("佚名"); + timeColumn.setCellValueFactory(cellData->cellData.getValue().timeStringProperty()); + senderColumn.setCellValueFactory(cellData->cellData.getValue().fromNameProperty()); + receiverColumn.setCellValueFactory(cellData->cellData.getValue().toNameProperty()); + msgColumn.setCellValueFactory(cellData->cellData.getValue().msgProperty()); + } + + @FXML + private void handleSendMessage() { + //点击发送按钮时执行的动作 + String jsonString=getSendRTMsgJson(); + if(jsonString.equals("")) { + //发送的消息为空或空白字符串 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("消息内容错误"); + alert.setHeaderText("你的消息内容为空"); + alert.setContentText("请检查你要发送的消息内容,消息内容不能为空或空白字符串."); + alert.showAndWait(); + }else { + //发送数据请求 + Long seqLong=mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(jsonString); + mainApp.minoaDataAPI.executeOrder("/sendRTMsg", orderInfo); + //获取用户列表信息 + ResponseData responseData=mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject=responseData.praseLoginData(); + System.out.println("/sendRTMsg-JSON: "+jsonObject.toString()); + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(codeInteger==200) { + //更新消息列表=>增加一条新的消息 + SingleMessage singleMessage=new SingleMessage(); + singleMessage.setFromName(mainApp.loginer.getUserName()); + singleMessage.setToName(this.contactName); + singleMessage.setTime(getNowSecondTimestamp()); + singleMessage.setMsg(sendMsgTextArea.getText().trim()); + mainApp.messageLogMap.get(this.contactName).add(singleMessage); + //消息发送成功,清空textarea + this.sendMsgTextArea.clear(); + }else { + //用户未登录,提示重新登录 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("登录信息过期"); + alert.setHeaderText("登录信息过期"); + alert.setContentText("你的登录信息过期,请重新登录。"); + alert.showAndWait(); + } + } + } + + //获取当前日期的时间戳[精确到秒] + private static Long getNowSecondTimestamp(){ + Date date=new Date(); + String timestamp = String.valueOf(date.getTime()/1000); + return Long.valueOf(timestamp); + } + + //获取发送消息时候的JSON字符串 + private String getSendRTMsgJson() { + String msgString=sendMsgTextArea.getText().trim(); + if(msgString.equals("")) { + return ""; + } + JSONObject jsonObject =new JSONObject(); + String commandString="/sendRTMsg"; + String fromString=mainApp.loginer.getUserName(); + String toString=this.contactName; + String uuidString=mainApp.loginer.getUuid(); + try { + jsonObject.put("command", commandString); + jsonObject.put("from", fromString); + jsonObject.put("to", toString); + jsonObject.put("uuid", uuidString); + jsonObject.put("msg", msgString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + //被mainApp调用 + this.mainApp = mainApp; + messageLogTable.setItems(mainApp.getCurrentMessageLogBySelectedPersonListData(contactName)); + contactNameLabel.setText(contactName); + } +} diff --git a/src/main/java/cn/minoa/view/message/MessageListController.java b/src/main/java/cn/minoa/view/message/MessageListController.java new file mode 100644 index 0000000..7cdbec0 --- /dev/null +++ b/src/main/java/cn/minoa/view/message/MessageListController.java @@ -0,0 +1,187 @@ +package cn.minoa.view.message; + +import cn.minoa.MainApp; +import cn.minoa.model.Person; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; +import javafx.scene.shape.Circle; +import javafx.scene.text.Text; + +public class MessageListController { + private MainApp mainApp; + // 携带数据请求命令的信息 +// private OrderInfo orderInfo; +// //刷新消息列表时的互斥锁 +// private ReentrantLock messageMutex; + @FXML + private ImageView message_list; + @FXML + private ListView contactListTable; + + public MessageListController() { + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + message_list.setImage(new Image("file:resources/images/message_list.png")); + // 监听选择的改变 + contactListTable.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showUserMessageDetails(newValue.getId())); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + //初始化Listview列表 + ObservableList personList = mainApp.getContactListData() ; + for(int i = 0 ; i filePathnameList; + // 当前文件列表对应的文件具体信息,key是filePathnameList中的表项 + private Map detailedFileListMap; + + // 树状列表:根节点=>对应根目录 + @FXML + private TreeView treeView; + // 根节点 + private TreeItem root; + //记录当前选择的文件夹目录节点 + private TreeItem currentTreeItem; + + @FXML + private Text pathText; + @FXML + private ListView fileList; + + + // 构造函数 + public MyCloudFileController() { + // TODO Auto-generated constructor stub + currentPath = "/"; + filePathnameList = new LinkedList(); + detailedFileListMap=new HashMap(); + currentTreeItem=new TreeItem<>(); + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + fileList.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showLowerLevel(newValue.getId())); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + // 发送命令请求主目录下的文件夹列表,赋值给filePathnameList + lsFolder(currentPath); + // 更新显示到前端 + pathText.setText("当前文件路径:/"); + for (int i = 0; i < filePathnameList.size(); i++) { + fileList.getItems().add(new FileListItem(filePathnameList.get(i)).getPaneItem()); + } + iniTreeView(); + System.out.println("设置默认选中文件夹:"+currentPath); + setSelectTreeItem(root,currentPath); + System.out.println("设置完毕"); + System.out.println("当前默认选中文件夹是:"+getPathBasedTreeNode(treeView.getSelectionModel().getSelectedItem())); + listenTreeView(); + } + +// // 隐藏掉share文件夹 +// private void hideShareInRoot(){ +// if(root==null){ +// return ; +// } +// for(int i=0;i treeItem,String path){ + if(path.equals("/")){ + treeView.getSelectionModel().select(treeItem); + currentTreeItem=treeItem; + }else if(!path.contains("/")){ + treeView.getSelectionModel().select(treeItem); + currentTreeItem=treeItem; + } + path=reduceFirstChar(path); + String foldername=getBeforeL(path); + // 遍历设置子节点 + for(int i=0;i "+res); + return res; + } + + //字符串操作:去掉第一个字符 + private String reduceFirstChar(String s){ + if(s.length()<=1){ + return ""; + }else{ + System.out.println("去掉字符串第一个字符: "+s+" => "+s.substring(1,s.length())); + return s.substring(1,s.length()); + } + } + + // 字符串操作:去掉第一个/字符之前的所有字符 + private String reduceBeforeFirstL(String s){ + if(!s.contains("/")){ + return ""; + } + int flag=0; + for(int i=0;i "+res); + return res; + } + + private final Node rootIcon = new ImageView( + new Image("file:resources/images/myFileIcon/d.png") + ); + + // 初始化一个treeview/treetableview + private void iniTreeView(){ + System.out.println("创建根节点: "+"/"); + // 创建根节点 + root=new TreeItem<>("/"); + System.out.println("设置根节点: "+root.getValue()); + treeView.setRoot(root); + currentTreeItem=root; + System.out.println("递归创建根节点的子节点: "+root.getValue()); + creatAllChildrenFolderByRecursion(root); +// System.out.println("设置孙子节点测试"); +// setChildrenNode(childNode1); + } + + // 设置文件树的监听事件 + private void listenTreeView(){ + System.out.println("设置treeview点击事件监听、选中项改变监听"); + treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Object oldValue, Object newValue) { + TreeItem item = (TreeItem) newValue; + if(item==null){ + System.out.println("Selected null"); + }else{ + System.out.println("Selected Folder Name: " + item.getValue()); + if(!getPathBasedTreeNode(currentTreeItem).equals(getPathBasedTreeNode(item))){ + currentPath=getPathBasedTreeNode(item); + currentTreeItem=item; + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + }); + treeView.setOnMouseClicked(new EventHandler() + { + @Override + public void handle(MouseEvent mouseEvent) { + TreeItem item = treeView.getSelectionModel().getSelectedItem(); + if(item==null){ + System.out.println("Selected null"); + }else{ + System.out.println("Selected Folder Name: " + item.getValue()); + if(!getPathBasedTreeNode(currentTreeItem).equals(getPathBasedTreeNode(item))){ + currentPath=getPathBasedTreeNode(item); + currentTreeItem=item; + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + }); + } + + // 递归创建某目录下的所有文件夹 + public boolean creatAllChildrenFolderByRecursion(TreeItem parentTreeItem){ + creatChildFolder(parentTreeItem); + if(parentTreeItem.getChildren().size()==0){ + return true; + } + // 遍历所有子节点 + for(int i=0;i parentTreeItem){ + System.out.println("创建某特定节点的子节点: "+getPathBasedTreeNode(parentTreeItem)); + // 创建子节点 + List childList=getChildFolder(getPathBasedTreeNode(parentTreeItem)); + for(int i=0;i childTreeItem=new TreeItem<>(childList.get(i)); + //封掉根目录下cache文件夹 + if(parentTreeItem.equals(root)&&childTreeItem.getValue().equals("cache")){ + }else{ + parentTreeItem.getChildren().add(childTreeItem); + } + } + if(childList.size()==0){ + parentTreeItem.setExpanded(false); + return false; + }else{ + parentTreeItem.setExpanded(true); + return true; + } + } + + // 传入一个树节点,给该树节点增加子节点,并重置treeView + private void setChildrenNode(TreeItem parentNode){ + TreeItem childNode = new TreeItem<>("Grand Child Node"); + parentNode.setExpanded(true); + parentNode.getChildren().add(childNode); + // 获取孙子节点路径 + System.out.println("获取孙子节点路径: "+childNode.getValue()); + getPathBasedTreeNode(childNode); + } + + // 获取某个文件目录下的子目录【只获取子文件夹】 + public List getChildFolder(String parentFolerPath){ + List childFolderPathList=new LinkedList(); + System.out.println("parentFolerPath: " + parentFolerPath); + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileActionJson("ls", parentFolerPath)); + System.out.println("getFileActionJson: " + getFileActionJson("ls", parentFolerPath)); + mainApp.minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if ((dataString.equals("")) || (dataString.equals("null"))) { + // 不做任何事情 + return childFolderPathList; + } + JSONArray dataJsonArray = new JSONArray(dataString); + String fileCacheString; + JSONObject fileCacheJsonObject; + // 遍历每个文件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + fileCacheString = dataJsonArray.getString(i); + fileCacheJsonObject = new JSONObject(fileCacheString); + String filename = fileCacheJsonObject.getString("filename"); + String type = fileCacheJsonObject.getString("type"); + if(type.equals("d")){ + childFolderPathList.add(filename); + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }else if(codeInteger==201){ + System.out.println("lsFolder: 201大数据传输接口"); + String dataString; + try { + dataString = jsonObject.getString("data"); + if (dataString.equals("")) { + //数据为空 + } else { + JSONObject dataJsonObject = new JSONObject(dataString); + String dataIdString = dataJsonObject.getString("dataId"); + Integer dataSizeInteger = dataJsonObject.getInt("dataSize"); + Integer sliceNumInteger = dataJsonObject.getInt("sliceNum"); + Integer sliceSizeInteger = dataJsonObject.getInt("sliceSize"); + byte[] noticelistByte = new byte[dataSizeInteger]; + for (Integer sliceNo = 0; sliceNo < sliceNumInteger; sliceNo++) { + // 发送数据请求 + Long seqLongT = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLongT); + System.out.println("getDataSlice: " + getGetDataSlice(dataIdString, sliceNo)); + orderInfo.setJsonString(getGetDataSlice(dataIdString, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/getDataSlice", orderInfo); + // 获取用户列表信息 + ResponseData responseDataCache = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLongT); + byte[] byteCache = responseDataCache.getFileSlice(); + System.out.println("byteCache size:" + byteCache.length); + System.out.println("byteCache:" + byteCache); + if (sliceNo == (sliceNumInteger - 1)) { + for (int x = (sliceNo * sliceSizeInteger); x < dataSizeInteger; x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } else { + for (int x = (sliceNo * sliceSizeInteger); x < (sliceNo * sliceSizeInteger + 7000); x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } + } + try { + String cacheString = new String(noticelistByte, "UTF-8"); + System.out.println(cacheString); + JSONArray dataJsonArray = new JSONArray(cacheString); + String fileCacheString; + JSONObject fileCacheJsonObject; + // 遍历每个文件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + fileCacheString = dataJsonArray.getString(i); + fileCacheJsonObject = new JSONObject(fileCacheString); + String filename = fileCacheJsonObject.getString("filename"); + String type = fileCacheJsonObject.getString("type"); + if(type.equals("d")){ + childFolderPathList.add(filename); + } + } + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return childFolderPathList; + } + + // 获取某个树节点的路径 + public String getPathBasedTreeNode(TreeItem treeItem){ + if(treeItem.getValue().equals("/")){ + return "/"; + } + TreeItem tempNode=treeItem; + String path=tempNode.getValue(); + while(true){ + if(tempNode.getParent().getValue().equals("/")){ + path=tempNode.getParent().getValue()+path; + }else{ + path=tempNode.getParent().getValue()+"/"+path; + } + tempNode=tempNode.getParent(); + if(tempNode.getValue().equals("/")){ + break; + } + } + System.out.println("final path: "+path); + return path; + } + + // 点击事件触发:展示下一级目录 + private void showLowerLevel(String paneId) { + // 判断是否是文件夹,是,则进入下一个界面 + if (checkFolder(paneId)) { + // 修改当前路径 + currentPath = getFolderName(paneId); + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else if (checkFile(paneId)) { + } + } + + // 返回上一级按钮 + @FXML + private void returnToUpperLevel() { + // 显示上一级目录信息 + if (!currentPath.equals("/")) { + // 获取上一级路径 + currentPath = getUpperLevelPath(currentPath); + pathText.setText("当前文件路径:" + currentPath); + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("无上一级目录"); + alert.setHeaderText("无法返回上一级"); + alert.setContentText("您已经到达文件根目录," + "无法返回上一级目录了哦~"); + alert.showAndWait(); + } + } + + // 上传文件到当前文件路径 + @FXML + private void uploadFileToCurrentPath() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择上传文件"); + Stage selectFileStage = new Stage(); + File file = fileChooser.showOpenDialog(selectFileStage); + if (file != null) { + // 文件不为空,且不是文件夹 + if (file.isFile()) { + //判断文件长度是否为0 + System.out.println("file length: "+file.length()); + if(file.length()==0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("空文件错误"); + alert.setHeaderText("您选择的是一个空文件"); + alert.setContentText("您选择的是一个空文件,请重新选择。"); + alert.showAndWait(); + return ; + } + System.out.println("开始文件上传... :" + file.getAbsolutePath()); +// 版1【默认根目录】: +// sendFileToServer(file); +// 版2【无进度条】: +// Integer r=mainApp.uploadFileToServerPath(file, currentPath + "/"); +// 版3【有进度条】: + if(currentPath.equals("/")){ + mainApp.uploadFileToServerPathWithProgressBar(file, currentPath); + }else{ + mainApp.uploadFileToServerPathWithProgressBar(file, currentPath + "/"); + } + // 判断是否手动取消了上传,是的话,重载界面,且不进行下面的判断 + if(mainApp.isCancelFileTransferFlag){ + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + e.printStackTrace(); + } + return; + } + // 上传文件之后,通过获取上传到服务器的文件大小,与本地文件大小对比 + // 判断是否传输成功 + String serverPathname; + if(currentPath.equals("/")){ + serverPathname=currentPath+file.getName(); + }else{ + serverPathname=currentPath+"/"+file.getName(); + } + Long fileServerLength=mainApp.getServerFileByteLength(serverPathname); + System.out.println("fileServerLength: "+fileServerLength); + if(fileServerLength<0){ + //文件未成功创建 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件上传失败"); + alert.setHeaderText("文件上传失败"); + alert.setContentText("由于未知原因,你的文件上传失败。"); + alert.showAndWait(); + }else if(fileServerLength== file.length()){ + //文件全部成功上传 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("文件上传成功"); + alert.setHeaderText("文件上传成功"); + alert.setContentText("你的文件已经成功上传至服务器。"); + alert.showAndWait(); + }else{ + //文件部分数据未成功写入 + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件上传失败"); + alert.setHeaderText("文件上传失败"); + alert.setContentText("由于未知原因,你的文件数据未全部上传成功。"); + alert.showAndWait(); + } + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件类型错误"); + alert.setHeaderText("你的文件类型错误"); + alert.setContentText("请确保你选中的是一个文件而非文件夹。"); + alert.showAndWait(); + } + } + } + + // 在当前文件目录下新建文件夹 + @FXML + private void mkdirOnCurrentPath() { + // 弹窗,请求输入文件夹名称 + mainApp.showNewFolderDialog(); + } + + // 删除文件夹操作 + @FXML + private void deleteFolder(){ + TreeItem item = treeView.getSelectionModel().getSelectedItem(); + if(item==null){ + System.out.println("Selected null"); + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("删除失败"); + alert.setHeaderText("删除文件夹失败"); + alert.setContentText("请在文件夹列表中先选中一个文件夹"); + alert.showAndWait(); + }else{ + System.out.println("Selected Folder Name: " + item.getValue()); + String foldername = getPathBasedTreeNode(item); + System.out.println("folder path name: "+foldername); + if(foldername.equals("/")){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("删除警告"); + alert.setHeaderText("删除文件夹警告"); + alert.setContentText("用户根目录不允许被删除!!!\n"); + alert.showAndWait(); + return ; + } + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileActionJson("rm", foldername)); + System.out.println("getFileActionJson: " + getFileActionJson("rm", foldername)); + mainApp.minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("删除成功"); + alert.setHeaderText("删除文件夹成功"); + alert.setContentText("你已成功删除一个文件夹:"+foldername); + alert.showAndWait(); + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(getUpperLevelPath(currentPath)); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("删除失败"); + alert.setHeaderText("删除文件夹失败"); + alert.setContentText("由于未知原因,您不能删除该选中的文件夹。"); + alert.showAndWait(); + } + } + } + + // 刷新当前文件列表 + @FXML + private void reloadOnCurrentPath() { + try { + mainApp.reloadCloudFileOverview(currentPath); +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.INFORMATION); +// alert.setTitle("刷新成功"); +// alert.setHeaderText("已成功刷新文件列表"); +// alert.setContentText("已成功刷新文件列表,当前是最新列表"); +// alert.showAndWait(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("刷新失败"); + alert.setHeaderText("刷新出现错误"); + alert.setContentText("由于未知原因,您的刷新出现错误。"); + alert.showAndWait(); + } + } + + public void makeDir(String foldername) { + //封死空文件夹 + if(foldername.trim().equals("")){ + return ; + } + // 封掉命名为/的文件夹 + if(foldername.trim().equals("/")){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("新建失败"); + alert.setHeaderText("新建文件夹失败"); + alert.setContentText("文件夹名称不许为\"/\"哦~"); + alert.showAndWait(); + return ; + } + foldername = currentPath + "/" + foldername; + //封死在用户跟目录下创建share或cache文件夹 + if(foldername.equals("/share")||foldername.equals("/cache")|| + foldername.equals("//share")||foldername.equals("//cache")){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("新建失败"); + alert.setHeaderText("新建文件夹失败"); + alert.setContentText("根目录下不允许创建命名为share或cache的文件夹。"); + alert.showAndWait(); + return ; + } + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileActionJson("mkdir", foldername)); + System.out.println("getFileActionJson: " + getFileActionJson("mkdir", foldername)); + mainApp.minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("新建成功"); + alert.setHeaderText("新建文件夹成功"); + alert.setContentText("已在当前目录下新建文件夹。"); + alert.showAndWait(); + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("新建失败"); + alert.setHeaderText("新建文件夹失败"); + alert.setContentText("由于未知原因,您在当前目录下不能新建文件夹。"); + alert.showAndWait(); + } + } + + // 判断字符串中是否有空格 + private boolean haveBlankSpaceInString(String s){ + for(int i=0;i只支持删除普通文件 + @FXML + private void deleteSelectedItem() { + //判断是否选择了文件 + if(fileList.getSelectionModel().getSelectedItem()==null){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件未选择"); + alert.setHeaderText("文件未选择"); + alert.setContentText("请先在文件列表中选择一个文件。"); + alert.showAndWait(); + return; + } + System.out.println("发送了删除请求到服务器..."); + // 获取选中的文件 + Pane pane = fileList.getSelectionModel().getSelectedItem(); + String pathfilename = getPathFileName(pane.getId()); + if (checkFile(pane.getId())) { + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileActionJson("rm", pathfilename)); + System.out.println("getFileActionJson: " + getFileActionJson("rm", pathfilename)); + mainApp.minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("删除成功"); + alert.setHeaderText("删除文件成功"); + alert.setContentText("已从云端删除您选中的文件。"); + alert.showAndWait(); + // 根据当前路径,重新加载整个界面,来刷新listview + try { + mainApp.reloadCloudFileOverview(currentPath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("删除失败"); + alert.setHeaderText("删除文件失败"); + alert.setContentText("由于未知原因,您选中的文件不能被删除"); + alert.showAndWait(); + } + } + + } + + // 下载选中项按钮,下载文件到缺省本地目录[E盘根目录] +// @FXML +// private void downloadSelectedItem() { +// Pane pane=fileList.getSelectionModel().getSelectedItem(); +// String pathfilename=getPathFileName(pane.getId()); +// System.out.println("开始下载... :"+pathfilename); +// int flag=mainApp.downloadFileByPathfilename(pathfilename); +// if(flag==0) { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.INFORMATION); +// alert.setTitle("下载成功"); +// alert.setHeaderText("下载文件成功"); +// alert.setContentText("您请求的文件数据已经被全部下载到本地," +// + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" +// + "您的文件默认保存位置为:"+mainApp.fileLocalPath); +// alert.showAndWait(); +// }else { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.ERROR); +// alert.setTitle("下载失败"); +// alert.setHeaderText("下载文件失败"); +// alert.setContentText("本地文件数据写入失败,请检查是否有权限访问默认存储位置。" +// + "您的文件默认保存位置为:"+mainApp.fileLocalPath); +// alert.showAndWait(); +// } +// } + + // 下载选中项按钮 + @FXML + private void downloadSelectedItem() { + //判断是否选择了文件 + if(fileList.getSelectionModel().getSelectedItem()==null){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("文件未选择"); + alert.setHeaderText("文件未选择"); + alert.setContentText("请先在文件列表中选择一个文件。"); + alert.showAndWait(); + return; + } + // 选择要下载到的文件夹 + String localDirString = mainApp.fileLocalPath; + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("选择要下载到的本地文件夹"); + Stage selectFileStage = new Stage(); + File file = directoryChooser.showDialog(selectFileStage); + if (file != null) { + // 文件不为空,且是一个文件夹 + if (file.isDirectory()) { + localDirString = file.getPath() + "\\"; + System.out.println("选中的本地保存文件夹为:" + localDirString); + } else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("选中类型错误"); + alert.setHeaderText("你的选中类型错误"); + alert.setContentText("请确保你选中的是一个文件夹而不是一个文件。"); + alert.showAndWait(); + return; + } + }else{ + return ; + } + + Pane pane = fileList.getSelectionModel().getSelectedItem(); + String pathfilename = getPathFileName(pane.getId()); + System.out.println("开始下载... :" + pathfilename); + mainApp.downloadFileByPathfilenameWithProgressBar(pathfilename,localDirString); + // 判断是否手动取消了下载,是的话就不进行下面的判断 + if(mainApp.isCancelFileTransferFlag){ + return; + } + // 下载文件之后,通过获取服务器的文件大小,与下载到本地的文件大小对比 + // 判断是否传输成功 + Long fileServerLength=mainApp.getServerFileByteLength(pathfilename); + System.out.println("fileServerLength: "+fileServerLength); + String localfilename=localDirString+getPureFileNameFromLinux(pathfilename); + File localfile=new File(localfilename); + if(!localfile.exists()){ + // 本地文件未被成功创建 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("本地文件未被成功创建,请检查是否有权限访问你选择的文件存储位置。" + "您的指定的文件保存位置为:" +localDirString); + alert.showAndWait(); + }else if(localfile.length()==fileServerLength){ + // 本地文件成功创建且字节长度与服务器一致 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("下载成功"); + alert.setHeaderText("下载文件成功"); + alert.setContentText("您请求的文件数据已经被全部下载到本地," + "您的文件保存位置为:" + + localDirString); + alert.showAndWait(); + }else{ + // 本地文件成功创建但字节长度与服务器不一致 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("文件数据未能全部写入本地。" + "您的文件保存位置为:" +localDirString); + alert.showAndWait(); + } +// int flag = mainApp.downloadFileByPathfilename(pathfilename,localDirString); +// if (flag == 0) { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.INFORMATION); +// alert.setTitle("下载成功"); +// alert.setHeaderText("下载文件成功"); +// alert.setContentText("您请求的文件数据已经被全部下载到本地," + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" + "您的文件保存位置为:" +// + localDirString); +// alert.showAndWait(); +// } else { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.ERROR); +// alert.setTitle("下载失败"); +// alert.setHeaderText("下载文件失败"); +// alert.setContentText("本地文件数据写入失败,请检查是否有权限访问文件存储位置。" + "您的文件保存位置为:" +localDirString); +// alert.showAndWait(); +// } + } + + // 根据包含路径的文件名获取纯文件名[linux下] + public String getPureFileNameFromLinux(String pathfilename) { + // 标记最后一个/的下标 + int indexLeft = -1; + for (int i = 0; i < pathfilename.length(); i++) { + if (pathfilename.charAt(i) == '/') { + indexLeft = i; + } + } + if (indexLeft == -1) { + System.out.println("pure name[linux]: " + pathfilename); + return pathfilename; + } else { + System.out.println("pure name[linux]: " + pathfilename.substring(indexLeft + 1, pathfilename.length())); + return pathfilename.substring(indexLeft + 1, pathfilename.length()); + } + } + + // 根据当前目录字符串获取上一层目录 + private String getUpperLevelPath(String path) { + String upperPathString = "/"; + // 标记最后一个/的下标 + int index = 0; + for (int i = 0; i < path.length(); i++) { + if (path.charAt(i) == '/') { + index = i; + } + } + if (index != 0) { + upperPathString = path.substring(0, index); + } + return upperPathString; + } + + // 文件操作命令 + private String getFileActionJson(String cmd, String filename) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/fileAction"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String cmdString = cmd; + String filenameString = filename; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("fileCmd", cmdString); + jsonObject.put("filename", filenameString); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 列出某目录下的所有文件【非文件夹】 + private boolean lsFolder(String foldername) { + System.out.println("罗列foldername下文件: " + foldername); + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getFileActionJson("ls", foldername)); + System.out.println("getFileActionJson: " + getFileActionJson("ls", foldername)); + mainApp.minoaDataAPI.executeOrder("/fileAction", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + filePathnameList.clear(); + detailedFileListMap.clear(); + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if ((dataString.equals("")) || (dataString.equals("null"))) { + filePathnameList.clear(); + detailedFileListMap.clear(); + return true; + } + JSONArray dataJsonArray = new JSONArray(dataString); + String fileCacheString; + JSONObject fileCacheJsonObject; + // 遍历每个文件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + fileCacheString = dataJsonArray.getString(i); + fileCacheJsonObject = new JSONObject(fileCacheString); + String filename = fileCacheJsonObject.getString("filename"); + String type = fileCacheJsonObject.getString("type"); + Long time=fileCacheJsonObject.getLong("time"); + Long size=fileCacheJsonObject.getLong("size"); + if (foldername.charAt(foldername.length() - 1) != '/'){ + foldername=foldername+"/"; + } + if(type.equals("f")){ + // filePathnameList + String fileId=foldername + filename + "@" + type; + filePathnameList.add(fileId); + // map + SingleFile singleFile=new SingleFile(fileId); + singleFile.setFilepath(foldername); + singleFile.setFilename(filename); + singleFile.setType(type); + singleFile.setSize(size); + singleFile.setTime(time); + detailedFileListMap.put(fileId,singleFile); + } + } + return true; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + }else if(codeInteger==201){ + System.out.println("lsFolder: 201大数据传输接口"); + String dataString; + try { + dataString = jsonObject.getString("data"); + if (dataString.equals("")) { + //数据为空 + } else { + JSONObject dataJsonObject = new JSONObject(dataString); + String dataIdString = dataJsonObject.getString("dataId"); + Integer dataSizeInteger = dataJsonObject.getInt("dataSize"); + Integer sliceNumInteger = dataJsonObject.getInt("sliceNum"); + Integer sliceSizeInteger = dataJsonObject.getInt("sliceSize"); + byte[] noticelistByte = new byte[dataSizeInteger]; + for (Integer sliceNo = 0; sliceNo < sliceNumInteger; sliceNo++) { + // 发送数据请求 + Long seqLongT = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLongT); + System.out.println("getDataSlice: " + getGetDataSlice(dataIdString, sliceNo)); + orderInfo.setJsonString(getGetDataSlice(dataIdString, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/getDataSlice", orderInfo); + // 获取用户列表信息 + ResponseData responseDataCache = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLongT); + byte[] byteCache = responseDataCache.getFileSlice(); + System.out.println("byteCache size:" + byteCache.length); + System.out.println("byteCache:" + byteCache); + if (sliceNo == (sliceNumInteger - 1)) { + for (int x = (sliceNo * sliceSizeInteger); x < dataSizeInteger; x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } else { + for (int x = (sliceNo * sliceSizeInteger); x < (sliceNo * sliceSizeInteger + 7000); x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } + } + try { + String cacheString = new String(noticelistByte, "UTF-8"); + System.out.println(cacheString); + JSONArray dataJsonArray = new JSONArray(cacheString); + String fileCacheString; + JSONObject fileCacheJsonObject; + // 遍历每个文件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + fileCacheString = dataJsonArray.getString(i); + fileCacheJsonObject = new JSONObject(fileCacheString); + String filename = fileCacheJsonObject.getString("filename"); + String type = fileCacheJsonObject.getString("type"); + Long time=fileCacheJsonObject.getLong("time"); + Long size=fileCacheJsonObject.getLong("size"); + if (foldername.charAt(foldername.length() - 1) != '/'){ + foldername=foldername+"/"; + } + if(type.equals("f")){ + // filePathnameList + String fileId=foldername + filename + "@" + type; + filePathnameList.add(fileId); + // map + SingleFile singleFile=new SingleFile(fileId); + singleFile.setFilepath(foldername); + singleFile.setFilename(filename); + singleFile.setType(type); + singleFile.setSize(size); + singleFile.setTime(time); + detailedFileListMap.put(fileId,singleFile); + } + } + return true; + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return false; + } + + // 请求每个通知的数据分片的JSON + public String getGetDataSlice(String dataId, Integer sliceNo) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getDataSlice"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer sliceNoInteger = sliceNo; + String dataIdString = dataId; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("dataId", dataIdString); + jsonObject.put("sliceNo", sliceNoInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + + // 获取上传文件的json格式 + private String getUploadFile(String filename, Integer offset) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/uploadFile"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + String filenameString = filename; + Integer offsetInteger = offset; + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("filename", filenameString); + jsonObject.put("offset", offsetInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } + +// //上传文件操作=>此函数只支持上传指定文件到用户根目录 +// public void sendFileToServer(File file) { +// // 获取要上传文件的文件长度,计算上传次数 +// long filelen = file.length(); +// int cycle = (int) filelen / mainApp.sliceSize; +// System.out.println("file-len: " + filelen); +// System.out.println("cycle: " + cycle); +// // 发送上传命令给上传接口,接口自行取本地数据 +// int flagSuccess = 0; // 记录收到200回复的次数 +// for (int i = 0; i <= cycle; i++) { +// OrderInfo orderInfo = new OrderInfo(); +// ResponseData responseData = new ResponseData(); +// Long seqLong = mainApp.getNewDataReqId(); +// orderInfo.setSeq(seqLong); +// // 此时传入的文件路径是windows下的绝对路径 +// orderInfo.setJsonString(getUploadFile(file.getAbsolutePath(), i * mainApp.sliceSize)); +// +// mainApp.minoaDataAPI.executeOrder("/uploadFile", orderInfo); +// responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); +//// System.out.println("上传文件的数据回执(200表示成功):"+responseData.getResponseJsonDataString()); +// JSONObject jsonObject = responseData.praseRequestData(); +// responseData.printSelf(); +// // 解析数据 +// Integer codeInteger = null; +// try { +// codeInteger = jsonObject.getInt("code"); +// } catch (JSONException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// if (codeInteger == 200) { +// flagSuccess++; +// } +// } +// // 每个分片都上传成功 +// if (flagSuccess == (cycle + 1)) { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.INFORMATION); +// alert.setTitle("上传成功"); +// alert.setHeaderText("上传文件成功"); +// alert.setContentText("您的文件已上传。"); +// alert.showAndWait(); +// // 根据当前路径,重新加载整个界面,来刷新listview +// try { +// mainApp.reloadCloudFileOverview(currentPath); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } else { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.WARNING); +// alert.setTitle("上传失败"); +// alert.setHeaderText("上传文件失败"); +// alert.setContentText("由于未知原因,您的文件上传失败,非常抱歉。"); +// alert.showAndWait(); +// } +// } + + // 从paneId中获取文件所在路径 + public String getPathFromPaneId(String paneId) { + // 标记最后一个/的下标 + int indexLeft = -1; + // 标记最后一个@的下标 + int indexRight = -1; + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + indexRight = i; + } else if (paneId.charAt(i) == '/') { + indexLeft = i; + } + } + if ((indexLeft == -1) || (indexRight == -1)) { + System.out.println("prase paneId error: " + paneId); + return ""; + } + return paneId.substring(0, indexLeft); + } + + // 从paneId中获取纯文件名 + public String getPureFilenameFromPaneId(String paneId) { + // 标记最后一个/的下标 + int indexLeft = -1; + // 标记最后一个@的下标 + int indexRight = -1; + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + indexRight = i; + } else if (paneId.charAt(i) == '/') { + indexLeft = i; + } + } + if ((indexLeft == -1) || (indexRight == -1)) { + System.out.println("prase paneId error: " + paneId); + return ""; + } + return paneId.substring(indexLeft + 1, indexRight); + } + + // 从paneId中获取文件类型 + private String getFileTypeFromPaneId(String paneId) { + return paneId.substring(paneId.length() - 1, paneId.length()); + } + + // 从paneId中获取文件夹路径[path+name] + private String getFolderName(String paneId) { + return paneId.substring(0, paneId.length() - 2); + } + + // 从paneId中获取文件路径[path+name] + private String getPathFileName(String paneId) { + return paneId.substring(0, paneId.length() - 2); + } + + // 判断是否是文件夹 + private boolean checkFolder(String paneId) { + if (getFileTypeFromPaneId(paneId).equals("d")) { + return true; + } else if (paneId.equals("/")) { + return true; + } + return false; + } + + // 判断是否是文件 + private boolean checkFile(String paneId) { + if (getFileTypeFromPaneId(paneId).equals("f")) { + return true; + } + return false; + } + + class FileListItem { + // 外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + // 头像 + private Button head; + private Integer headLength; + // 文本区域:文件名 + private Text text; + // 文件大小 + private Text filesizeText; + // 文件创建时间 + private Text filetimeText; + + // 每个Pane代映射了一个文件[路径+文件名+@文件类型] + String pathfilename; + + // 组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + private Integer fileszTextXInteger; + private Integer fileszTextYInteger; + private Integer filetimeTextXInteger; + private Integer filetimeTextYInteger; + + // 设置联系人头像 + private void setHeadPortrait(Button button, String filetype) { + button.setStyle( + String.format("-fx-background-image: url('file:resources/images/myFileIcon/%s.png')", filetype)); + } + + // 构造函数 + public FileListItem(String pathfiletypename) { + // 映射数据 + this.pathfilename = pathfiletypename; + // 大小配置 + paneWidth = 200; + paneHeight = 50; + headLength = 35; + // 位置配置 + headXInteger = 10; + headYInteger = 10; + textXInteger = 70; + textYInteger = 28; + fileszTextXInteger=textXInteger+150; + fileszTextYInteger=textYInteger; + filetimeTextXInteger=textXInteger+280; + filetimeTextYInteger=textYInteger; + + pane = new Pane(); + head = new Button(); + text = new Text(); + filesizeText=new Text(); + filetimeText=new Text(); + + // 最外面的pane的大小 + pane.setPrefSize(paneWidth, paneHeight); + + // 设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + + // 设置消息头像和消息文本属性 + head.setPrefSize(headLength, headLength); + } + + // 根据person获取pane + public Pane getPaneItem() { + String name = this.pathfilename; + // 头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + // text位置 + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + filesizeText.setLayoutX(fileszTextXInteger); + filesizeText.setLayoutY(fileszTextYInteger); + filetimeText.setLayoutX(filetimeTextXInteger); + filetimeText.setLayoutY(filetimeTextYInteger); + // 头像和气泡文本内容 + String thisfilename=getPureFilenameFromPaneId(name); + if(StringByteLengthUtil.getByteLength(thisfilename)>20){ + thisfilename=thisfilename.substring(0,10)+" ..."; + } + text.setText(thisfilename); + setHeadPortrait(head, getFileTypeFromPaneId(name)); + // 文件大小 + Long size=detailedFileListMap.get(name).getSize(); + filesizeText.setText(FileUtil.sizeToShowSize(size)); + // 文件创建时间 + Long time=detailedFileListMap.get(name).getTime(); + filetimeText.setText(FileUtil.timeToDateString(time)); + // 文件大小和文件创建时间 + pane.getChildren().add(head); + pane.getChildren().add(text); + pane.getChildren().add(filesizeText); + pane.getChildren().add(filetimeText); + // 设置pane的id + pane.setId(name); + return pane; + } + + // 根据包含路径的文件名获取纯文件名 + private String getPureFilenameFromPaneId(String paneId) { + // 标记最后一个/的下标 + int indexLeft = -1; + // 标记最后一个@的下标 + int indexRight = -1; + for (int i = 0; i < paneId.length(); i++) { + if (paneId.charAt(i) == '@') { + indexRight = i; + } else if (paneId.charAt(i) == '/') { + indexLeft = i; + } + } + if ((indexLeft == -1) || (indexRight == -1)) { + System.out.println("prase paneId error: " + paneId); + return ""; + } + return paneId.substring(indexLeft + 1, indexRight); + } + + // 从paneId中获取文件类型 + private String getFileTypeFromPaneId(String paneId) { + return paneId.substring(paneId.length() - 1, paneId.length()); + } + } +} + diff --git a/src/main/java/cn/minoa/view/mine/MyEmailController.java b/src/main/java/cn/minoa/view/mine/MyEmailController.java new file mode 100644 index 0000000..0fce177 --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/MyEmailController.java @@ -0,0 +1,458 @@ +package cn.minoa.view.mine; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.LinkedList; +import java.util.List; + +import cn.minoa.model.SingleNotice; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.Email; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; + +public class MyEmailController { + private MainApp mainApp; + private List emailInfoList; + private final String CONTROLSTRING_SEND; + private final String CONTROLSTRING_RECEIVE; + + @FXML + private Text listTitleText; + @FXML + private ListView emailList; + + public MyEmailController() { + // TODO Auto-generated constructor stub + emailInfoList = new LinkedList(); + CONTROLSTRING_RECEIVE = "R"; + CONTROLSTRING_SEND = "S"; + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + emailList.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> showEmailDetails(Integer.parseInt(newValue.getId()))); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + // 被mainApp调用 + this.mainApp = mainApp; + // 缺省标题 + listTitleText.setText("收件箱列表"); + // 初始化list + getEmail(1); + //修改状态 + mainApp.currentEmailPane = mainApp.MAIN_EMAILPANE_REC; + // 反应到前端 + for (int i = 0; i < emailInfoList.size(); i++) { + emailList.getItems().add(new EmailListItem(emailInfoList.get(i)).getPaneItem()); + } + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp, String controlString) { + // 被mainApp调用 + this.mainApp = mainApp; + if (controlString.equals(CONTROLSTRING_RECEIVE)) { + // 缺省标题 + listTitleText.setText("收件箱列表"); + // 初始化list + getEmail(1); + //修改状态 + mainApp.currentEmailPane = mainApp.MAIN_EMAILPANE_REC; + // 反应到前端 + for (int i = 0; i < emailInfoList.size(); i++) { + emailList.getItems().add(new EmailListItem(emailInfoList.get(i)).getPaneItem()); + } + } else { + // 缺省标题 + listTitleText.setText("发件箱列表"); + // 初始化list + getEmail(0); + //修改状态 + mainApp.currentEmailPane = mainApp.MAIN_EMAILPANE_SEND; + // 反应到前端 + for (int i = 0; i < emailInfoList.size(); i++) { + emailList.getItems().add(new EmailListItem(emailInfoList.get(i)).getPaneItem()); + } + } + } + + // 切换成发件箱 + @FXML + private void switchToSendBox() { + try { + mainApp.reloadEmailOverview(CONTROLSTRING_SEND); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 切换成发件箱 + @FXML + private void switchToRecvBox() { + try { + mainApp.reloadEmailOverview(CONTROLSTRING_RECEIVE); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // 展示邮件的详细信息的弹窗 + private void showEmailDetails(Integer emailId) { +// printEmailIdList(); +// System.out.println("current emailId: "+emailId); + // 在当前数据结构中找到这个emailId对应的邮件 +// Email email = new Email(); + for (int i = 0; i < emailInfoList.size(); i++) { +// System.out.println("current emailInfoList emailId: "+emailInfoList.get(i).getId()); + if (emailInfoList.get(i).getId().equals(emailId)) { + mainApp.showEmailDetailsDialog(emailInfoList.get(i)); + break; + } + } + // 传递给主函数 +// mainApp.showEmailDetailsDialog(email); + } + + //打印测试 + private void printEmailIdList() { + for (int i = 0; i < emailInfoList.size(); i++) { + System.out.println(i + "[emailId]: " + emailInfoList.get(i).getId()); + } + } + + // 写信弹窗 + @FXML + private void handleWritter() { + mainApp.showWritterEmailDialog(); + } + + //刷新列表 + @FXML + private void refreshList() { + if (mainApp.currentMainPane.equals(mainApp.MAIN_EMAILPANE) && mainApp.currentEmailPane.equals(mainApp.MAIN_EMAILPANE_SEND)) { + //面板显示发件箱 + try { + mainApp.reloadEmailOverview("S"); + } catch (IOException e) { + e.printStackTrace(); + } + } else if (mainApp.currentMainPane.equals(mainApp.MAIN_EMAILPANE) && mainApp.currentEmailPane.equals(mainApp.MAIN_EMAILPANE_REC)) { + //面板显示收件箱 + try { + mainApp.reloadEmailOverview("R"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // 发送数据请求请求发送给登录者的邮件列表 更新emailInfoList + private Integer getEmail(Integer queryCode) { + // 请求数据 + OrderInfo orderInfo = new OrderInfo(); + ResponseData responseData = new ResponseData(); + Long seqLong = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLong); + orderInfo.setJsonString(getGetApproval(queryCode)); + mainApp.minoaDataAPI.executeOrder("/getApproval", orderInfo); + responseData = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLong); + System.out.println("邮件回执:"); + responseData.printSelf(); + JSONObject jsonObject = responseData.praseRequestData(); + responseData.printSelf(); + // 解析数据 + Integer codeInteger = null; + try { + codeInteger = jsonObject.getInt("code"); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (codeInteger == 200) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if ((dataString.equals("")) || (dataString.equals("null"))) { + emailInfoList.clear(); + return 0; + } + emailInfoList.clear(); + JSONArray dataJsonArray = new JSONArray(dataString); + String emailCacheString; + JSONObject emailCacheJsonObject; + // 遍历每个邮件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + emailCacheString = dataJsonArray.getString(i); + emailCacheJsonObject = new JSONObject(emailCacheString); + Integer id = emailCacheJsonObject.getInt("approvalId"); + String senderInfo = emailCacheJsonObject.getString("proposer"); + // 解析发送者和接收者的姓名 + String receiverInfo = emailCacheJsonObject.getString("approver"); + JSONObject receiverJsonObject = new JSONObject(receiverInfo); + JSONObject senderJsonObject = new JSONObject(senderInfo); + String sender_usernameString = senderJsonObject.getString("username"); + String receiver_usernameString = receiverJsonObject.getString("username"); + // 解析邮件主题和邮件内容 + String detailString = emailCacheJsonObject.getString("detail"); + System.out.println("邮件详细: " + detailString); + detailString = detailString.substring(1, detailString.length() - 1); + System.out.println("处理后的邮件详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + String title = detailJsonObject.getString("title"); + String content = detailJsonObject.getString("content"); + //解析邮件的附件列表 + String filesString = detailJsonObject.getString("files"); + JSONArray filesJsonArray = new JSONArray(filesString); + List emailfiles = new LinkedList(); + for (int j = 0; j < filesJsonArray.length(); j++) { + String onefileString = filesJsonArray.getString(j); + JSONObject onefileJsonObject = new JSONObject(onefileString); + String filename = onefileJsonObject.getString("filename"); + emailfiles.add(filename); + } + //构建一个邮件对象 + Email email = new Email(); + email.setId(id); + email.setTitle(title); + email.setContent(content); + email.setSender(sender_usernameString); + email.setReceiver(receiver_usernameString); + email.files = emailfiles; + emailInfoList.add(email); + } + return 0; + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return -1; + } + } else if (codeInteger == 201) { + String dataString; + try { + dataString = jsonObject.getString("data"); + if (dataString.equals("")) { + //数据为空 + } else { + JSONObject dataJsonObject = new JSONObject(dataString); + String dataIdString = dataJsonObject.getString("dataId"); + Integer dataSizeInteger = dataJsonObject.getInt("dataSize"); + Integer sliceNumInteger = dataJsonObject.getInt("sliceNum"); + Integer sliceSizeInteger = dataJsonObject.getInt("sliceSize"); + byte[] noticelistByte = new byte[dataSizeInteger]; + for (Integer sliceNo = 0; sliceNo < sliceNumInteger; sliceNo++) { + // 发送数据请求 + Long seqLongT = mainApp.getNewDataReqId(); + orderInfo.setSeq(seqLongT); + System.out.println("getDataSlice: " + mainApp.getGetDataSlice201(dataIdString, sliceNo)); + orderInfo.setJsonString(mainApp.getGetDataSlice201(dataIdString, sliceNo)); + mainApp.minoaDataAPI.executeOrder("/getDataSlice", orderInfo); + // 获取用户列表信息 + ResponseData responseDataCache = mainApp.minoaDataAPI.dataCacheQueue.getResponseDataBySeq(seqLongT); + byte[] byteCache = responseDataCache.getFileSlice(); + System.out.println("byteCache size:" + byteCache.length); + System.out.println("byteCache:" + byteCache); + if (sliceNo == (sliceNumInteger - 1)) { + for (int x = (sliceNo * sliceSizeInteger); x < dataSizeInteger; x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } else { + for (int x = (sliceNo * sliceSizeInteger); x < (sliceNo * sliceSizeInteger + 7000); x++) { + noticelistByte[x] = byteCache[x - (sliceNo * sliceSizeInteger)]; + } + } + } + try { + String cacheString = new String(noticelistByte, "UTF-8"); + System.out.println(cacheString); + //解析邮件列表 + JSONArray dataJsonArray = new JSONArray(cacheString); + String emailCacheString; + JSONObject emailCacheJsonObject; + // 遍历每个邮件信息 + for (int i = 0; i < dataJsonArray.length(); i++) { + emailCacheString = dataJsonArray.getString(i); + emailCacheJsonObject = new JSONObject(emailCacheString); + Integer id = emailCacheJsonObject.getInt("approvalId"); + String senderInfo = emailCacheJsonObject.getString("proposer"); + // 解析发送者和接收者的姓名 + String receiverInfo = emailCacheJsonObject.getString("approver"); + JSONObject receiverJsonObject = new JSONObject(receiverInfo); + JSONObject senderJsonObject = new JSONObject(senderInfo); + String sender_usernameString = senderJsonObject.getString("username"); + String receiver_usernameString = receiverJsonObject.getString("username"); + // 解析邮件主题和邮件内容 + String detailString = emailCacheJsonObject.getString("detail"); + System.out.println("邮件详细: " + detailString); + detailString = detailString.substring(1, detailString.length() - 1); + System.out.println("处理后的邮件详细: " + detailString); + JSONObject detailJsonObject = new JSONObject(detailString); + String title = detailJsonObject.getString("title"); + String content = detailJsonObject.getString("content"); + //解析邮件的附件列表 + String filesString = detailJsonObject.getString("files"); + JSONArray filesJsonArray = new JSONArray(filesString); + List emailfiles = new LinkedList(); + for (int j = 0; j < filesJsonArray.length(); j++) { + String onefileString = filesJsonArray.getString(j); + JSONObject onefileJsonObject = new JSONObject(onefileString); + String filename = onefileJsonObject.getString("filename"); + emailfiles.add(filename); + } + //构建一个邮件对象 + Email email = new Email(); + email.setId(id); + email.setTitle(title); + email.setContent(content); + email.setSender(sender_usernameString); + email.setReceiver(receiver_usernameString); + email.files = emailfiles; + emailInfoList.add(email); + } + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return -1; + } + } + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return -1; + } + return 0; + } else { + return -1; + } + } + + // 获取邮件记录命令: 0表示查询我发送的,1表示查询发送给我的 + private String getGetApproval(Integer queryCode) { + JSONObject jsonObject = new JSONObject(); + String commandString = "/getApproval"; + String usernameString = mainApp.loginer.getUserName(); + String uuidString = mainApp.loginer.getUuid(); + Integer typeCodeInteger = 3; + Integer queryCodeInteger = queryCode; + Integer pageNoInteger = 0; // 页编号 + Integer pageSizeInteger = 100; // 页大小 + try { + jsonObject.put("command", commandString); + jsonObject.put("username", usernameString); + jsonObject.put("uuid", uuidString); + jsonObject.put("typeCode", typeCodeInteger); + jsonObject.put("queryCode", queryCodeInteger); + jsonObject.put("pageNo", pageNoInteger); + jsonObject.put("pageSize", pageSizeInteger); + } catch (Exception e) { + // TODO: handle exception + System.out.println("exception: " + e.getMessage()); + } + return jsonObject.toString(); + } +} + +class EmailListItem { + // 外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + // 头像 + private Button head; + private Integer headLength; + // 文本区域 + private Text text; // 显示邮件发送者姓名 + private Text titleText; // 显示邮件标题 + + private Email email; + + // 组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + private Integer titleTextXInteger; + private Integer titleTextYInteger; + + // 设置联系人头像 + private void setHeadPortrait(Button button, String filetype) { + button.setStyle( + String.format("-fx-background-image: url('file:resources/images/headPhotos/%s.png')", filetype)); + } + + // 构造函数 + public EmailListItem(Email email) { + // 数据配置 + this.email = email; + // 大小配置 + paneWidth = 200; + paneHeight = 50; + headLength = 35; + // 位置配置 + headXInteger = 10; + headYInteger = 10; + textXInteger = 70; + textYInteger = 28; + titleTextXInteger = 170; + titleTextYInteger = 28; + + pane = new Pane(); + head = new Button(); + text = new Text(); + titleText = new Text(); + + // 最外面的pane的大小 + pane.setPrefSize(paneWidth, paneHeight); + + // 设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + + // 设置消息头像和消息文本属性 + head.setPrefSize(headLength, headLength); + } + + // 根据email获取pane + public Pane getPaneItem() { + // 头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + // 发送人名位置 + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + // 邮件标题位置 + titleText.setLayoutX(titleTextXInteger); + titleText.setLayoutY(titleTextYInteger); + // 头像和气泡文本内容 + text.setText(email.getSender()); + titleText.setText(email.getTitle()); + setHeadPortrait(head, email.getSender()); + // 赋值子节点 + pane.getChildren().add(head); + pane.getChildren().add(text); + pane.getChildren().add(titleText); + // 设置pane的id + pane.setId(email.getId().toString()); + return pane; + } +} diff --git a/src/main/java/cn/minoa/view/mine/MyNoticeController.java b/src/main/java/cn/minoa/view/mine/MyNoticeController.java new file mode 100644 index 0000000..e5d3950 --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/MyNoticeController.java @@ -0,0 +1,66 @@ +package cn.minoa.view.mine; + +import cn.minoa.MainApp; +import cn.minoa.model.SingleNotice; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; + +public class MyNoticeController { + private MainApp mainApp; + + @FXML + private ListView noticeListView; + + public MyNoticeController() { + // TODO Auto-generated constructor stub + } + + @FXML + private void initialize() { + //在fxml被载入时候被自动调用 + noticeListView.setCellFactory(param->new ListCell() { + @Override + protected void updateItem(SingleNotice item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null || item.getContent() == null) { + setText(null); + } else { + String rawtitle=item.getTitle(); + if(rawtitle.length()>15) { + setText(rawtitle.substring(0, 15)+"..."); + }else { + setText(rawtitle); + } + } + + } + }); + //监听选择的改变 + noticeListView.getSelectionModel().selectedItemProperty().addListener( + (observable,oldValue,newValue)->showMyNoticeDetails(newValue)); + + } + + @FXML + public void handleNewNotice() { + //自己发布一个通知 + mainApp.showWritterNoticeDialog(); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + //被mainApp调用 + this.mainApp = mainApp; + noticeListView.setItems(mainApp.getNoticeListData()); + } + + //展示该通知的详细信息 + public void showMyNoticeDetails(SingleNotice singleNotice) { + //显示一个弹窗,里面展示该通知的详细信息 + mainApp.showSingleNoticeDialog(singleNotice); + } + +} diff --git a/src/main/java/cn/minoa/view/mine/NewFolderDialogController.java b/src/main/java/cn/minoa/view/mine/NewFolderDialogController.java new file mode 100644 index 0000000..156ae65 --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/NewFolderDialogController.java @@ -0,0 +1,44 @@ +package cn.minoa.view.mine; +//文件夹名称接收页面 + +import cn.minoa.MainApp; +import javafx.fxml.FXML; +import javafx.scene.control.TextField; +import javafx.stage.Stage; + +public class NewFolderDialogController { + private MainApp mainApp; + private Stage dialogStage; + @FXML + private TextField newFolderName; + + + @FXML + private void initialize() { + } + + /* + * @param dialogStage + */ + public void setDialogStage(Stage dialogStage) { + this.dialogStage = dialogStage; + } + + public void setMainApp(MainApp mainApp) { + this.mainApp=mainApp; + } + + /* + * called when the user clicks cancel. + */ + @FXML + private void handleExit() { + String newFolderNameString=""; + if(!newFolderName.getText().trim().equals("")) { + newFolderNameString=newFolderName.getText().trim(); + } + //调用新建文件夹函数 + mainApp.newFolder(newFolderNameString); + dialogStage.close(); + } +} diff --git a/src/main/java/cn/minoa/view/mine/SingleEmailDetailsController.java b/src/main/java/cn/minoa/view/mine/SingleEmailDetailsController.java new file mode 100644 index 0000000..b1886d4 --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/SingleEmailDetailsController.java @@ -0,0 +1,239 @@ +package cn.minoa.view.mine; + +import java.io.File; + +import cn.minoa.MainApp; +import cn.minoa.model.Email; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.layout.Pane; +import javafx.scene.text.Text; +import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +public class SingleEmailDetailsController { + private MainApp mainApp; + private Stage dialogStage; + + @FXML + private Text senderText; + @FXML + private Text receiverText; + @FXML + private Text titleText; + @FXML + private TextArea contentText; + + @FXML + private Text emailFilesTitleText; + @FXML + private ListView emailFiles; + + public SingleEmailDetailsController() { + // TODO Auto-generated constructor stub + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + // 监听选择的改变 + emailFiles.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> downloadEmailFile(newValue.getId())); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + public void setEmailInfo(Email email) { + senderText.setText(email.getSender()+"@pkusz.edu.cn"); + receiverText.setText(email.getReceiver()+"@pkusz.edu.cn"); + titleText.setText(email.getTitle()); + contentText.setText(email.getContent()); + // 如果邮件带有附件,将其显示到前端 + showEmailFiles(email); + } + + private void showEmailFiles(Email email) { + if (email.getFilesNumber() == 0) { + // 没有文件,隐藏附件列表 + // ... + } else { + // 有文件,展示到前端 + for (int i = 0; i < email.getFilesNumber(); i++) { + emailFiles.getItems().add(new emailFileListItem(email.files.get(i)).getPaneItem()); + } + } + } + +// //从服务器下载选中的附件 +// private void downloadEmailFile(String pathfilename) { +// System.out.println("开始下载... :"+pathfilename); +// int flag=mainApp.downloadFileByPathfilename(pathfilename); +// if(flag==0) { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.INFORMATION); +// alert.setTitle("下载成功"); +// alert.setHeaderText("下载文件成功"); +// alert.setContentText("您请求的文件数据已经被全部下载到本地," +// + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" +// + "您的文件默认保存位置为:"+mainApp.fileLocalPath); +// alert.showAndWait(); +// }else { +//// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ +// Alert alert = new Alert(AlertType.ERROR); +// alert.setTitle("下载失败"); +// alert.setHeaderText("下载文件失败"); +// alert.setContentText("本地文件数据写入失败,请检查是否有权限访问默认存储位置。" +// + "您的文件默认保存位置为:"+mainApp.fileLocalPath); +// alert.showAndWait(); +// } +// } + + //从服务器下载选中的附件到指定的本地文件夹下 + private void downloadEmailFile(String pathfilename) { + //选择要下载到的文件夹 + String localDirString=mainApp.fileLocalPath; + DirectoryChooser directoryChooser=new DirectoryChooser(); + directoryChooser.setTitle("选择要下载到的本地文件夹"); + Stage selectFileStage = new Stage(); + File file = directoryChooser.showDialog(selectFileStage); + if (file != null) { + //文件不为空,且不是文件夹 + if(file.isDirectory()) { + localDirString=file.getPath()+"\\"; + System.out.println("选中的文件夹为:"+localDirString); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("选中类型错误"); + alert.setHeaderText("你的选中类型错误"); + alert.setContentText("请确保你选中的是一个文件夹而不是一个文件。"); + alert.showAndWait(); + } + }else{ + return ; + } + + System.out.println("开始下载... :"+pathfilename); +// int flag=mainApp.downloadFileByPathfilename(pathfilename,localDirString); + int flag=-1; + if(flag==0) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.INFORMATION); + alert.setTitle("下载成功"); + alert.setHeaderText("下载文件成功"); + alert.setContentText("您请求的文件数据已经被全部下载到本地," + + "如有发现打开异常或数据内容并非所求,说明出现了服务器数据请求问题。" + + "您的文件保存位置为:"+localDirString); + alert.showAndWait(); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("下载失败"); + alert.setHeaderText("下载文件失败"); + alert.setContentText("本地文件数据写入失败,请检查是否有权限访问默认存储位置。" + + "您的文件默认保存位置为:"+localDirString); + alert.showAndWait(); + } + } + + + public void setDialogStage(Stage stage) { + this.dialogStage = stage; + } + + @FXML + private void exit() { + this.dialogStage.close(); + } +} + +//邮件附件 +class emailFileListItem { + // 外部pane + private Pane pane; + private Integer paneWidth; + private Integer paneHeight; + // 头像 + private Button head; + private Integer headLength; + // 文本区域 +// public TextArea text; + private Text text; +// private Integer textWidth; +// private Integer textHeight; + + // 每个Pane代映射了一个文件[路径+文件名+@文件类型] + String pathfilename; + + // 组件位置属性 + private Integer headXInteger; + private Integer headYInteger; + private Integer textXInteger; + private Integer textYInteger; + + // 设置联系人头像 + private void setHeadPortrait(Button button, String filetype) { + button.setStyle( + String.format("-fx-background-image: url('file:resources/images/emailFileIcon/%s.png')", filetype)); + } + + // 构造函数 + public emailFileListItem(String pathfiletypename) { + // 映射数据 + this.pathfilename = pathfiletypename; + // 大小配置 + paneWidth = 200; + paneHeight = 50; + headLength = 35; +// textWidth = 150; +// textHeight = 25; + // 位置配置 + headXInteger = 10; + headYInteger = 10; + textXInteger = 70; + textYInteger = 28; + + pane = new Pane(); + head = new Button(); + text = new Text(); + + // 最外面的pane的大小 + pane.setPrefSize(paneWidth, paneHeight); + + // 设置头像和pane的CSS的样式标识 + head.getStyleClass().add("head"); + pane.getStyleClass().add("pane"); + + // 设置消息头像和消息文本属性 + head.setPrefSize(headLength, headLength); + } + + // 根据person获取pane + public Pane getPaneItem() { + String name = this.pathfilename; + // 头像位置 + head.setLayoutY(headYInteger); + head.setLayoutX(headXInteger); + // text位置 + text.setLayoutX(textXInteger); + text.setLayoutY(textYInteger); + // 头像和气泡文本内容 + text.setText(name); + setHeadPortrait(head, "f"); + pane.getChildren().add(head); + pane.getChildren().add(text); + // 设置pane的id + pane.setId(name); + return pane; + } +} diff --git a/src/main/java/cn/minoa/view/mine/SingleNoticeDetailsController.java b/src/main/java/cn/minoa/view/mine/SingleNoticeDetailsController.java new file mode 100644 index 0000000..b38b84f --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/SingleNoticeDetailsController.java @@ -0,0 +1,53 @@ +package cn.minoa.view.mine; + +import cn.minoa.model.SingleNotice; +import javafx.fxml.FXML; +import javafx.scene.control.TextArea; +import javafx.scene.text.Text; +import javafx.stage.Stage; + +public class SingleNoticeDetailsController { + @FXML + private Text userNameField; + @FXML + private Text titleField; + @FXML + private TextArea contentField; + + private Stage dialogStage; + @SuppressWarnings("unused") + private SingleNotice singleNotice; + + @FXML + private void initialize() { + } + + /* + * @param dialogStage + */ + public void setDialogStage(Stage dialogStage) { + this.dialogStage = dialogStage; + } + + /* + * @param person + */ + public void setNotice(SingleNotice singleNotice) { + this.singleNotice = singleNotice; + userNameField.setText(singleNotice.getUsername()); + titleField.setText(singleNotice.getSimpleTitle()); + contentField.setText(singleNotice.getContent()); +// birthdayField.setText(DateUtil.format(person.getBirthday())); +// 显示提示文本(日期格式) +// birthdayField.setPromptText("dd.mm.yyyy"); + } + + /* + * called when the user clicks cancel. + */ + @FXML + private void handleExit() { + dialogStage.close(); + } + +} diff --git a/src/main/java/cn/minoa/view/mine/WrittenEmailController.java b/src/main/java/cn/minoa/view/mine/WrittenEmailController.java new file mode 100644 index 0000000..6af81d8 --- /dev/null +++ b/src/main/java/cn/minoa/view/mine/WrittenEmailController.java @@ -0,0 +1,348 @@ +package cn.minoa.view.mine; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import cn.minoa.MainApp; +import cn.minoa.dataRequestInterface.OrderInfo; +import cn.minoa.dataRequestInterface.ResponseData; +import cn.minoa.model.Person; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.Alert; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.Pane; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import javafx.scene.control.Alert.AlertType; + +public class WrittenEmailController { + + private MainApp mainApp; + private Stage dialogStage; + + @FXML + private TextField receiverField; + @FXML + private TextField emailTitleField; + @FXML + private TextArea emailContentTextArea; + + @FXML + private ListView emailFiles; + //与之对应的数据结构 + private List emailFilesList; + + public WrittenEmailController() { + // TODO Auto-generated constructor stub + emailFilesList=new LinkedList(); + } + + @FXML + private void initialize() { + // 在fxml被载入时候被自动调用 + receiverField.setPromptText("请输入邮件地址(格式:昵称@pkusz.edu.cn)"); + emailTitleField.setPromptText("请输入邮件主题..."); + emailContentTextArea.setPromptText("请输入邮件内容..."); + } + + /* + * @param mainApp + */ + public void setMainApp(MainApp mainApp) { + this.mainApp = mainApp; + } + + public void setStage(Stage stage) { + this.dialogStage = stage; + } + + /* + * 从弹窗获取要添加的附件文件路径,将其加入到附件列表中,并显示到前端 + */ + @FXML + private void addFilesToEmail() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择上传文件"); + Stage selectFileStage = new Stage(); + File file = fileChooser.showOpenDialog(selectFileStage); + if (file != null) { + //文件不为空,且不是文件夹 + if(file.isFile()) { + //判断文件长度是否为0 + System.out.println("file length: "+file.length()); + if(file.length()==0){ + // 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("空文件错误"); + alert.setHeaderText("您选择的是一个空文件"); + alert.setContentText("您选择的是一个空文件,请重新选择。"); + alert.showAndWait(); + return ; + } + if(!isSelected(file.getAbsolutePath())) { + emailFilesList.add(file.getAbsolutePath()); + emailFiles.getItems().add(new emailFileListItem(file.getAbsolutePath()).getPaneItem()); + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("附件添加重复"); + alert.setHeaderText("你选择的该文件已加入附件列表"); + alert.setContentText("请确保你选中的文件是未加入附件列表的文件。"); + alert.showAndWait(); + } + }else { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("附件类型错误"); + alert.setHeaderText("你的文件类型错误"); + alert.setContentText("请确保你选中的是一个文件而非文件夹。"); + alert.showAndWait(); + } + } + } + + //判断邮件地址是否符合要求 + private boolean isRightEmailPath(String emailPath){ + //取尾部字段:@pkusz.edu.cn [length=13] + if(emailPath.length()<=13){ + return false; + } + String tailPath=emailPath.substring(emailPath.length()-13); + System.out.println("tailPath: "+tailPath); + if(tailPath.equals("@pkusz.edu.cn")){ + return true; + }else{ + return false; + } + } + + //取邮件地址中的昵称字段 + private String getUserInEmailPath(String emailPath){ + return emailPath.substring(0,emailPath.length()-13); + } + + //根据文件路径判断该选中文件是否已经加入到列表中 + private boolean isSelected(String absolutePath) { + boolean flag=false; + for(int i=0;i persons=mainApp.contactListData; + for (Iterator iterator = persons.iterator(); iterator.hasNext();) { + Person person = (Person) iterator.next(); + if(person.getUserName().equals(receiverName)) { + flag++; + } + } + if(flag==0) { + //弹窗提示 +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("收件人错误"); + alert.setHeaderText("你的收件人错误"); + alert.setContentText("请确保你输入的收件人姓名是系统的实际用户。"); + alert.showAndWait(); + return false; + } + //验证标题是否为空 + if(emailTitleField.getText().trim().equals("")) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("邮件标题错误"); + alert.setHeaderText("你的邮件标题错误"); + alert.setContentText("邮件标题不能为空。"); + alert.showAndWait(); + return false; + } + //验证内容是否为空 + if(emailContentTextArea.getText().trim().equals("")) { +// 弹窗参考:https://code.makery.ch/blog/javafx-dialogs-official/ + Alert alert = new Alert(AlertType.WARNING); + alert.setTitle("邮件内容错误"); + alert.setHeaderText("你的邮件内容错误"); + alert.setContentText("邮件内容不能为空。"); + alert.showAndWait(); + return false; + } + return true; + } + + //发送邮件命令 + private String getSendApproval() { + boolean flag=true; //记录上传附件是否成功 + JSONObject detailJsonObject = new JSONObject(); + String titleString=emailTitleField.getText().trim(); + String contentString=emailContentTextArea.getText().trim(); + //根据已经添加的文件附件列表制作一个arraylist + JSONArray filesArray=new JSONArray(); + for(int i=0;i + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
diff --git a/src/main/resources/fxml/cloudfile/FileShare.css b/src/main/resources/fxml/cloudfile/FileShare.css new file mode 100644 index 0000000..3a79b4c --- /dev/null +++ b/src/main/resources/fxml/cloudfile/FileShare.css @@ -0,0 +1,47 @@ +/*list样式 + * */ +.list-cell:empty { + -fx-background-color: white; +} +/*页面底色 +*/ +#upPane{ + -fx-background-color:#E1E6F6; +} +/* + * 文件图标样式 + */ +.head{ + -fx-background-radius: 1px; + -fx-background-position: center center; + -fx-background-repeat: no-repeat; + -fx-background-size: 40px 40px; + -fx-background-color: rgba(255,255,255,0); + -fx-background-image: url("file:resources/images/myFileIcon/o.png"); + -fx-cursor: hand; + -fx-border-width: 1px; + -fx-border-radius: 1px; + -fx-border-color:transparent; +} +.head:hover{ + -fx-background-radius: 1px; + -fx-background-position: center center; + -fx-background-repeat: no-repeat; + -fx-background-color: transparent; + -fx-background-size: 40px 40px; + -fx-cursor: hand; + -fx-border-width: 1px; + -fx-border-radius: 1px; + -fx-border-color:transparent; +} +.head:pressed{ + -fx-background-radius: 1px; + -fx-background-position: center center; + -fx-background-repeat: no-repeat; + -fx-background-color: transparent; + -fx-background-size: 40px 40px; + -fx-cursor: hand; + -fx-border-width: 1px; + -fx-border-radius: 1px; + -fx-border-color:transparent; +} \ No newline at end of file diff --git a/src/main/resources/fxml/cloudfile/FileShareOverview.fxml b/src/main/resources/fxml/cloudfile/FileShareOverview.fxml new file mode 100644 index 0000000..61d5d13 --- /dev/null +++ b/src/main/resources/fxml/cloudfile/FileShareOverview.fxml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/home/DealSingleReimbursement.fxml b/src/main/resources/fxml/home/DealSingleReimbursement.fxml new file mode 100644 index 0000000..b5c1ac7 --- /dev/null +++ b/src/main/resources/fxml/home/DealSingleReimbursement.fxml @@ -0,0 +1,33 @@ + + + + + + + + + + + +