update to v1.0.23 优化了永久资源链接功能,增强保密性

This commit is contained in:
kohgylw 2019-10-05 11:11:25 +08:00
parent 93c1bfc7bf
commit 1172011315
23 changed files with 357 additions and 91 deletions

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kohgylw.kiftd.server.mapper.PropertiesMapper">
<resultMap id="BaseResultMap" type="kohgylw.kiftd.server.model.Propertie">
<id column="propertie_key" jdbcType="VARCHAR" property="propertieKey" />
<result column="propertie_value" jdbcType="VARCHAR" property="propertieValue" />
</resultMap>
<insert id="insert" parameterType="kohgylw.kiftd.server.model.Propertie">
INSERT INTO PROPERTIES
VALUES(#{propertieKey,jdbcType=VARCHAR},#{propertieValue,jdbcType=VARCHAR})
</insert>
<update id="update" parameterType="kohgylw.kiftd.server.model.Propertie">
UPDATE PROPERTIES SET propertie_value =
#{propertieValue,jdbcType=VARCHAR} WHERE propertie_key =
#{propertieKey,jdbcType=VARCHAR}
</update>
<delete id="deleteByKey" parameterType="java.lang.String">
DELETE FROM PROPERTIES WHERE
propertie_key = #{propertieKey,jdbcType=VARCHAR}
</delete>
<select id="selectByKey" parameterType="java.lang.String"
resultMap="BaseResultMap">
SELECT * FROM PROPERTIES WHERE propertie_key =
#{propertieKey,jdbcType=VARCHAR}
</select>
</mapper>

View File

@ -13,48 +13,53 @@ import org.springframework.core.io.*;
/**
*
* <h2>服务器部分数据接入设置</h2>
* <p>该配置类定义了服务器组件使用的MyBatis将如何链接数据库如需更换其他数据库请在此配置自己的数据源并替换原有数据源</p>
* <p>
* 该配置类定义了服务器组件使用的MyBatis将如何链接数据库如需更换其他数据库请在此配置自己的数据源并替换原有数据源
* </p>
*
* @author 青阳龙野(kohgylw)
* @version 1.0
*/
@Configurable
public class DataAccess
{
private static Resource[] mapperFiles;
private static Resource mybatisConfg;
@Bean
public DataSource dataSource() {
final DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(ConfigureReader.instance().getFileNodePathDriver());
ds.setUrl(ConfigureReader.instance().getFileNodePathURL());
ds.setUsername(ConfigureReader.instance().getFileNodePathUserName());
ds.setPassword(ConfigureReader.instance().getFileNodePathPassWord());
return (DataSource)ds;
}
@Bean(name = { "sqlSessionFactory" })
@Autowired
public SqlSessionFactoryBean sqlSessionFactoryBean(final DataSource ds) {
final SqlSessionFactoryBean ssf = new SqlSessionFactoryBean();
ssf.setDataSource(ds);
ssf.setConfigLocation(DataAccess.mybatisConfg);
ssf.setMapperLocations(DataAccess.mapperFiles);
return ssf;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
final MapperScannerConfigurer msf = new MapperScannerConfigurer();
msf.setBasePackage("kohgylw.kiftd.server.mapper");
msf.setSqlSessionFactoryBeanName("sqlSessionFactory");
return msf;
}
static {
final String mybatisResourceFolder = ConfigureReader.instance().getPath() + File.separator + "mybatisResource" + File.separator;
final String mapperFilesFolder = mybatisResourceFolder + "mapperXML" + File.separator;
DataAccess.mapperFiles = new Resource[] { new FileSystemResource(mapperFilesFolder + "NodeMapper.xml"), new FileSystemResource(mapperFilesFolder + "FolderMapper.xml") };
DataAccess.mybatisConfg = (Resource)new FileSystemResource(mybatisResourceFolder + "mybatis.xml");
}
public class DataAccess {
private static Resource[] mapperFiles;
private static Resource mybatisConfg;
@Bean
public DataSource dataSource() {
final DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(ConfigureReader.instance().getFileNodePathDriver());
ds.setUrl(ConfigureReader.instance().getFileNodePathURL());
ds.setUsername(ConfigureReader.instance().getFileNodePathUserName());
ds.setPassword(ConfigureReader.instance().getFileNodePathPassWord());
return (DataSource) ds;
}
@Bean(name = { "sqlSessionFactory" })
@Autowired
public SqlSessionFactoryBean sqlSessionFactoryBean(final DataSource ds) {
final SqlSessionFactoryBean ssf = new SqlSessionFactoryBean();
ssf.setDataSource(ds);
ssf.setConfigLocation(DataAccess.mybatisConfg);
ssf.setMapperLocations(DataAccess.mapperFiles);
return ssf;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
final MapperScannerConfigurer msf = new MapperScannerConfigurer();
msf.setBasePackage("kohgylw.kiftd.server.mapper");
msf.setSqlSessionFactoryBeanName("sqlSessionFactory");
return msf;
}
static {
final String mybatisResourceFolder = ConfigureReader.instance().getPath() + File.separator + "mybatisResource"
+ File.separator;
final String mapperFilesFolder = mybatisResourceFolder + "mapperXML" + File.separator;
DataAccess.mapperFiles = new Resource[] { new FileSystemResource(mapperFilesFolder + "NodeMapper.xml"),
new FileSystemResource(mapperFilesFolder + "FolderMapper.xml"),
new FileSystemResource(mapperFilesFolder + "PropertiesMapper.xml") };
DataAccess.mybatisConfg = (Resource) new FileSystemResource(mybatisResourceFolder + "mybatis.xml");
}
}

View File

@ -41,7 +41,7 @@ public class ExternalLinksController {
@RequestMapping("/chain/{fileName}")
public void chain(HttpServletRequest request,HttpServletResponse response) {
fcs.getResourceBychain(request, response);
fcs.getResourceByChainKey(request, response);
}
}

View File

@ -37,6 +37,8 @@ public class HomeController {
private ShowPictureService sps;
@Resource
private PlayAudioService pas;
@Resource
private FileChainService fcs;
@RequestMapping({ "/getServerOS.ajax" })
@ResponseBody
@ -263,18 +265,25 @@ public class HomeController {
public String pong(final HttpServletRequest request) {
return as.doPong(request);
}
// 询问是否开启自由注册新账户功能
@RequestMapping(value = { "/askForAllowSignUpOrNot.ajax" }, produces = { CHARSET_BY_AJAX })
@ResponseBody
public String askForAllowSignUpOrNot(final HttpServletRequest request) {
return as.isAllowSignUp();
}
// 处理注册新账户请求
@RequestMapping(value = { "/doSigUp.ajax" }, produces = { CHARSET_BY_AJAX })
@ResponseBody
public String doSigUp(final HttpServletRequest request) {
return as.doSignUp(request);
}
// 获取永久资源链接的对应ckey
@RequestMapping(value = { "/getFileChainKey.ajax" }, produces = { CHARSET_BY_AJAX })
@ResponseBody
public String getFileChainKey(final HttpServletRequest request) {
return fcs.getChainKeyByFid(request);
}
}

View File

@ -0,0 +1,15 @@
package kohgylw.kiftd.server.mapper;
import kohgylw.kiftd.server.model.Propertie;
public interface PropertiesMapper {
int insert(final Propertie p);
int deleteByKey(final String propertieKey);
Propertie selectByKey(final String propertieKey);
int update(final Propertie p);
}

View File

@ -0,0 +1,28 @@
package kohgylw.kiftd.server.model;
/**
*
* <h2>文件系统相关设置项的模型</h2>
* <p>该模型用于描述文件系统数据库中的PROPERTIES表</p>
* @author 青阳龙野(kohgylw)
* @version 1.0
*/
public class Propertie {
private String propertieKey;
private String propertieValue;
public String getPropertieKey() {
return propertieKey;
}
public void setPropertieKey(String propertieKey) {
this.propertieKey = propertieKey;
}
public String getPropertieValue() {
return propertieValue;
}
public void setPropertieValue(String propertieValue) {
this.propertieValue = propertieValue;
}
}

View File

@ -14,12 +14,22 @@ public interface FileChainService {
/**
*
* <h2>根据链接返回资源数据</h2>
* <p>该方法将通过永久资源链接返回其指向的数据并声明其可能的ContentType类型若未开启永久资源链接功能则返回403</p>
* <h2>根据文件ID获取其永久资源链接的ckey</h2>
* <p>该方法用于获取加密的ckey以便在使用资源链接时声明其指向的文件</p>
* @author 青阳龙野(kohgylw)
* @param request javax.servlet.http.HttpServletRequest 请求对象
* @return java.lang.String 获取的ckey若获取失败则返回ERROR
*/
public String getChainKeyByFid(HttpServletRequest request);
/**
*
* <h2>根据链接中的ckey返回对应的资源数据</h2>
* <p>该方法将通过永久资源链接返回其ckey指向的文件数据并声明其可能的ContentType类型若未开启永久资源链接功能则返回403</p>
* @author 青阳龙野(kohgylw)
* @param request javax.servlet.http.HttpServletRequest 请求对象
* @param response javax.servlet.http.HttpServletResponse 响应对象
*/
public void getResourceBychain(HttpServletRequest request,HttpServletResponse response);
public void getResourceByChainKey(HttpServletRequest request,HttpServletResponse response);
}

View File

@ -9,12 +9,19 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import kohgylw.kiftd.server.enumeration.AccountAuth;
import kohgylw.kiftd.server.mapper.FolderMapper;
import kohgylw.kiftd.server.mapper.NodeMapper;
import kohgylw.kiftd.server.mapper.PropertiesMapper;
import kohgylw.kiftd.server.model.Folder;
import kohgylw.kiftd.server.model.Node;
import kohgylw.kiftd.server.model.Propertie;
import kohgylw.kiftd.server.service.FileChainService;
import kohgylw.kiftd.server.util.AESCipher;
import kohgylw.kiftd.server.util.ConfigureReader;
import kohgylw.kiftd.server.util.ContentTypeMap;
import kohgylw.kiftd.server.util.FileBlockUtil;
import kohgylw.kiftd.server.util.FolderUtil;
import kohgylw.kiftd.server.util.LogUtil;
import kohgylw.kiftd.server.util.RangeFileStreamWriter;
@ -24,39 +31,57 @@ public class FileChainServiceImpl extends RangeFileStreamWriter implements FileC
@Resource
private NodeMapper nm;
@Resource
private FolderMapper flm;
@Resource
private FileBlockUtil fbu;
@Resource
private ContentTypeMap ctm;
@Resource
private LogUtil lu;
@Resource
private AESCipher cipher;
@Resource
private PropertiesMapper pm;
@Resource
private FolderUtil fu;
@Override
public void getResourceBychain(HttpServletRequest request, HttpServletResponse response) {
public void getResourceByChainKey(HttpServletRequest request, HttpServletResponse response) {
int statusCode = 403;
if (ConfigureReader.instance().isOpenFileChain()) {
final String fId = request.getParameter("fid");
final String ckey = request.getParameter("ckey");
// 权限凭证有效性并确认其对应的资源
if (fId != null) {
Node f = this.nm.queryById(fId);
if (f != null) {
File target = this.fbu.getFileFromBlocks(f);
if (target != null && target.isFile()) {
String fileName = f.getFileName();
String suffix = "";
if (fileName.indexOf(".") >= 0) {
suffix = fileName.substring(fileName.indexOf("."));
if (ckey != null) {
Propertie keyProp = pm.selectByKey("chain_aes_key");
if (keyProp != null) {
try {
String fid = cipher.decrypt(keyProp.getPropertieValue(), ckey);
Node f = this.nm.queryById(fid);
if (f != null) {
File target = this.fbu.getFileFromBlocks(f);
if (target != null && target.isFile()) {
String fileName = f.getFileName();
String suffix = "";
if (fileName.indexOf(".") >= 0) {
suffix = fileName.substring(fileName.indexOf("."));
}
writeRangeFileStream(request, response, target, f.getFileName(),
ctm.getContentType(suffix));
if (request.getHeader("Range") == null) {
this.lu.writeChainEvent(request, f);
}
return;
}
}
writeRangeFileStream(request, response, target, f.getFileName(), ctm.getContentType(suffix));
if (request.getHeader("Range") == null) {
this.lu.writeChainEvent(request, f);
}
return;
statusCode = 404;
} catch (Exception e) {
lu.writeException(e);
statusCode = 500;
}
} else {
statusCode = 404;
}
statusCode = 404;
}
} else {
statusCode = 403;
}
try {
//  处理无法下载的资源
@ -66,4 +91,41 @@ public class FileChainServiceImpl extends RangeFileStreamWriter implements FileC
}
}
@Override
public String getChainKeyByFid(HttpServletRequest request) {
if (ConfigureReader.instance().isOpenFileChain()) {
String fid = request.getParameter("fid");
String account = (String) request.getSession().getAttribute("ACCOUNT");
if (fid != null) {
final Node f = this.nm.queryById(fid);
if (f != null) {
if (ConfigureReader.instance().authorized(account, AccountAuth.DOWNLOAD_FILES,
fu.getAllFoldersId(f.getFileParentFolder()))) {
Folder folder = flm.queryById(f.getFileParentFolder());
if (ConfigureReader.instance().accessFolder(folder, account)) {
// 将指定的fid加密为ckey并返回
try {
Propertie keyProp = pm.selectByKey("chain_aes_key");
if (keyProp == null) {// 如果没有生成过永久性AES密钥则先生成再加密
String aesKey = cipher.generateRandomKey();
Propertie chainAESKey = new Propertie();
chainAESKey.setPropertieKey("chain_aes_key");
chainAESKey.setPropertieValue(aesKey);
if (pm.insert(chainAESKey) > 0) {
return cipher.encrypt(aesKey, fid);
}
} else {// 如果已经有了则直接用其加密
return cipher.encrypt(keyProp.getPropertieValue(), fid);
}
} catch (Exception e) {
lu.writeException(e);
}
}
}
}
}
}
return "ERROR";
}
}

View File

@ -0,0 +1,78 @@
package kohgylw.kiftd.server.util;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.stereotype.Component;
/**
*
* <h2>AES加密器</h2>
* <p>该加密器负责对字符串进行加密解密和随机密码生产操作详见其中的各个方法注释</p>
* @author 青阳龙野(kohgylw)
* @version 1.0
*/
@Component
public class AESCipher {
private static final String CIPHER_TYPE = "AES";//所用到的加密算法类型
private Base64.Encoder encoder;
private Base64.Decoder decoder;
public AESCipher() {
encoder = Base64.getEncoder();
decoder = Base64.getDecoder();
}
/**
*
* <h2>生成随机AES密钥</h2>
* <p>该方法将生成一个随机的AES密钥并转化为Base64字符串返回</p>
* @author 青阳龙野(kohgylw)
* @return java.lang.String 随机生成的密钥以Base64字符串输出
*/
public String generateRandomKey() throws NoSuchAlgorithmException {
KeyGenerator kg = KeyGenerator.getInstance(CIPHER_TYPE);
kg.init(128);
return encoder.encodeToString(kg.generateKey().getEncoded());
}
/**
*
* <h2>对字符串进行AES加密</h2>
* <p>该方法用于对传入的字符串进行加密并以Base64的形式返回加密后的密文加密用到的密钥也需以Base64的形式传入</p>
* @author 青阳龙野(kohgylw)
* @param base64Key java.lang.String 加密密钥必须以Base64形式传入
* @param content java.lang.String 需要加密的内容
* @return java.lang.String 加密后的密文以Base64的形式返回
*/
public String encrypt(String base64Key, String content) throws Exception {
SecretKey key = new SecretKeySpec(decoder.decode(base64Key), CIPHER_TYPE);
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.ENCRYPT_MODE, key);
return encoder.encodeToString(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
/**
*
* <h2>对密文进行AES解密</h2>
* <p>该方法用于对传入的Base64形式的密文进行解密解密用到的密钥也需以Base64的形式传入</p>
* @author 青阳龙野(kohgylw)
* @param base64Key java.lang.String 解密密钥必须以Base64形式传入
* @param ciphertext java.lang.String 需要解密的密文必须以Base64形式传入
* @return java.lang.String 解密后的内容
*/
public String decrypt(String base64Key, String ciphertext) throws Exception {
SecretKey key = new SecretKeySpec(decoder.decode(base64Key), CIPHER_TYPE);
Cipher cipher = Cipher.getInstance(CIPHER_TYPE);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(decoder.decode(ciphertext)),StandardCharsets.UTF_8);
}
}

View File

@ -81,15 +81,20 @@ public class FileNodeUtil {
ResultSet indexCount = state4.executeQuery("SHOW INDEX FROM FILE WHERE Key_name = 'file_index'");
if (!indexCount.next()) {
final Statement state41 = conn.createStatement();
state41.execute("CREATE INDEX file_index ON FILE (file_id,file_name)");
state41.execute("CREATE INDEX file_index ON FILE (file_name)");
state41.close();
}
state4.close();
} else {
final Statement state4 = conn.createStatement();
state4.execute("CREATE INDEX IF NOT EXISTS file_index ON FILE (file_id,file_name)");
state4.execute("CREATE INDEX IF NOT EXISTS file_index ON FILE (file_name)");
state4.close();
}
// 生成用于持久化保存的系统自动生成的和文件系统相关设置项的存储表
final Statement state5 = conn.createStatement();
state5.execute(
"CREATE TABLE IF NOT EXISTS PROPERTIES(propertie_key VARCHAR(128) PRIMARY KEY,propertie_value VARCHAR(128) NOT NULL)");
state5.close();
}
Printer.instance.print("文件节点初始化完毕。");
} catch (Exception e) {

View File

@ -1,5 +1,5 @@
#Generated by Maven Integration for Eclipse
#Fri Oct 04 16:01:13 CST 2019
#Sat Oct 05 11:10:33 CST 2019
version=1.0.23-RELEASE
groupId=kohgylw
m2e.projectName=kiftd

View File

@ -800,7 +800,7 @@
<textarea id="fileChainTextarea" class="form-control" rows="3" readonly></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" onclick="copyFileChain()">复制链接</button>
<button id="copyChainBtn" type="button" class="btn btn-info" onclick="copyFileChain()">复制链接</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>

View File

@ -392,7 +392,7 @@ function getServerOS() {
url : "homeController/getServerOS.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
return;
}
$("#serverOS").text(result);
@ -421,7 +421,7 @@ function showFolderView(fid,targetId) {
$("#publishTime").html("<span class='graytext'>获取失败,请尝试刷新</span>");
$("#parentlistbox").html("<span class='graytext'>获取失败,请尝试刷新</span>");
} else if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else if(result == "NOT_FOUND") {
document.cookie = "folder_id=" + escape("root");// 归位记忆路径
window.location.href="/";
@ -1038,7 +1038,7 @@ function createfolder() {
url : "homeController/newFolder.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
showFolderAlert("提示:您的操作未被授权,创建文件夹失败。");
@ -1104,7 +1104,7 @@ function deleteFolder(folderId) {
url : "homeController/deleteFolder.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
$('#deleteFolderMessage').text("提示:您的操作未被授权,删除文件夹失败");
@ -1172,7 +1172,7 @@ function renameFolder(folderId) {
url : "homeController/renameFolder.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
showRFolderAlert("提示:您的操作未被授权,编辑失败。");
@ -1287,7 +1287,7 @@ function checkUploadFile() {
url : "homeController/checkUploadFile.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "errorParameter") {
showUploadFileAlert("提示:参数不正确,无法开始上传");
@ -1541,7 +1541,7 @@ function deleteFile(fileId) {
url : "homeController/deleteFile.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
$('#deleteFileMessage').text("提示:您的操作未被授权,删除失败");
@ -1598,7 +1598,7 @@ function renameFile(fileId) {
url : "homeController/renameFile.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "cannotRenameFile") {
showRFileAlert("提示:出现意外错误,可能未能重命名文件,请刷新后重试。");
@ -1992,7 +1992,7 @@ function deleteAllChecked() {
url : "homeController/deleteCheckedFiles.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
$('#deleteFileMessage').text("提示:您的操作未被授权,删除失败");
@ -2259,7 +2259,7 @@ function doMoveFiles(){
url : "homeController/confirmMoveFiles.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
$('#moveFilesMessage').text("提示:您的操作未被授权,移动失败");
@ -2353,7 +2353,7 @@ function sendMoveFilesReq(){
url : "homeController/moveCheckedFiles.ajax",
success : function(result) {
if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else {
if (result == "noAuthorized") {
$('#moveFilesMessage').text("提示:您的操作未被授权,移动失败");
@ -2455,7 +2455,7 @@ function selectInCompletePath(keyworld){
$("#publishTime").html("<span class='graytext'>获取失败,请尝试刷新</span>");
$("#parentlistbox").html("<span class='graytext'>获取失败,请尝试刷新</span>");
} else if (result == "mustLogin") {
window.location.href = "login.html";
window.location.href = "prv/login.html";
} else if(result == "notAccess"){
document.cookie = "folder_id=" + escape("root");
window.location.href="/";
@ -2630,7 +2630,7 @@ function checkImportFolder(){
showImportFolderAlert("提示:参数不正确,无法开始上传");
break;
case 'mustLogin':
window.location.href = "login.html";
window.location.href = "prv/login.html";
break;
case 'fileOverSize':
showImportFolderAlert("提示:文件["+ifs[maxFileIndex].webkitRelativePath+"]的体积超过最大限制("+resJson.maxSize+"),无法开始上传");
@ -2962,11 +2962,37 @@ function changePasswordGetNewVerCode(){
$("#changepassword_showvercode").attr("src","homeController/getNewVerCode.do?s="+(new Date()).getTime());
}
// 显示永久资源链接
// 获取永久资源链接
function getFileChain(fileId,fileName){
var getChainFileName=fileName.replace("#","%23").replace("%","%25").replace("?","%3F");
$("#fileChainTextarea").text(encodeURI(window.location.protocol+"//"+window.location.host+"/externalLinksController/chain/"+getChainFileName+"?fid="+fileId));
$("#fileChainTextarea").text("正在获取……");
$("#copyChainBtn").attr('disabled', true);
$('#fileChainModal').modal('show');
$.ajax({
type : "POST",
dataType : "text",
url : "homeController/getFileChainKey.ajax",
data : {
fid : fileId
},
success : function(result) {
switch (result) {
case "ERROR":
$("#fileChainTextarea").text("提示:获取失败,请刷新页面或稍后再试。");
break;
case "mustlogin":
window.location.href = "prv/login.html";
break;
default:
var getChainFileName=fileName.replace("#","%23").replace("%","%25").replace("?","%3F");
$("#fileChainTextarea").text(encodeURI(window.location.protocol+"//"+window.location.host+"/externalLinksController/chain/"+getChainFileName+"?ckey=")+encodeURIComponent(result));
$("#copyChainBtn").attr('disabled', false);
break;
}
},
error : function() {
$("#fileChainTextarea").text("提示:获取失败,无法连接服务器。");
}
});
}
// 复制链接内容
@ -2975,14 +3001,12 @@ function copyFileChain(){
let issafariBrowser = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
if(issafariBrowser){
node.setSelectionRange(0, 9999);
}
else{
}else{
const range = document.createRange();
range.selectNode(node);
const selection = window.getSelection();
if(selection.rangeCount > 0) selection.removeAllRanges();
selection.addRange(range);
}
document.execCommand('copy');
}

File diff suppressed because one or more lines are too long