mirror of
https://github.com/KOHGYLW/kiftd-source.git
synced 2025-01-09 04:27:56 +08:00
update to v1.0.17 重构了下载链接判定逻辑,使其能够直接下载
This commit is contained in:
parent
a480c79d94
commit
be003781de
7
TODO.txt
7
TODO.txt
@ -62,7 +62,7 @@ kiftd项目 计划表-2018-10-9 by 青阳龙野
|
||||
【已完成】文件管理——新增了一个较为强大的本地文件管理器,或者说“文件夹视图”,现在终于能够直接在本地操作kiftd文件系统中的文件了!
|
||||
【已完成】增加存储路径显示——显示在一个文本框内。
|
||||
【已完成】优化了下载过程——将原本的NIO读取换回简单的RandomAccessFile读取。
|
||||
【已完成】将音乐播放器的播放不播放同步。
|
||||
【已完成】修正了音乐播放器的播放不播放同步问题。
|
||||
【已完成】修正了断点续传中下载日志会被重复记录的问题。
|
||||
【已完成】修正了在Safari浏览器中下载中文文件名时出现乱码的问题。
|
||||
【已完成】MySQL数据库——针对一些对数据库要求较高的用户,现在提供将外部MySQL作为文件节点存储数据库的功能。
|
||||
@ -100,9 +100,12 @@ kiftd项目 计划表-2018-10-9 by 青阳龙野
|
||||
|
||||
计划版本 v1.0.17
|
||||
--------------
|
||||
【已完成】所有常见的视频文件格式均已支持在线播放——这是针对在线播放模块呼声最高的建议。
|
||||
通过在在线播放模块中集成JAVE多媒体解码器,现在,用户可以直接在线播放mp4/mov/wmv/flv/mkv/webm/avi格式的全部视频了。
|
||||
【已完成】名副其实的分享下载链接——现在,指定文件的下载链接将动态生成,持有该链接的用户将可直接下载指定资源(无需再进行权限认证),支持跨域请求。
|
||||
【已完成】优化了上传文件的权限检查机制,现在上传文件不会由于时间过长而导致权限失效了。
|
||||
【已完成】文件列表中文件名过长时能够自动换行,以避免文字溢出。
|
||||
【已完成】新增了“返回顶部”按钮——当用户浏览得过于深入底部时,这个按钮可以将其快速带回。
|
||||
【计划中】增加更多视频格式支持,引入自动转码模块。
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
package kohgylw.kiftd.server.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import kohgylw.kiftd.server.service.ExternalDownloadService;
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>外部链接控制器</h2>
|
||||
* <p>该控制器主要处理来自外部链接的请求,例如用于分享的下载链接。该控制器内的所有请求均允许跨域。</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
* @version 1.0
|
||||
*/
|
||||
@Controller
|
||||
@CrossOrigin
|
||||
@RequestMapping({"/externalLinksController"})
|
||||
public class ExternalLinksController {
|
||||
|
||||
@Resource
|
||||
private ExternalDownloadService eds;//分享下载链接的相关处理
|
||||
|
||||
@RequestMapping("/getDownloadKey.ajax")
|
||||
public @ResponseBody String getDownloadKey(HttpServletRequest request) {
|
||||
return eds.getDownloadKey(request);
|
||||
}
|
||||
|
||||
@RequestMapping("/downloadFileByKey.do")
|
||||
public void downloadFileByKey(HttpServletRequest request,HttpServletResponse response) {
|
||||
eds.downloadFileByKey(request, response);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package kohgylw.kiftd.server.service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>外部下载链接处理服务</h2>
|
||||
* <p>该服务主要包含了生成、获取外部下载链接以及使用它们进行下载的相关操作。详见各个方法。</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface ExternalDownloadService {
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>获取一个下载凭证</h2>
|
||||
* <p>针对指定资源获取一个下载凭证,要求该凭证的签收者必须具备下载权限。该凭证在服务器关闭前将一直有效。</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
* @param request javax.servlet.http.HttpServletRequest 请求对象
|
||||
* @return java.lang.String 下载凭证
|
||||
*/
|
||||
String getDownloadKey(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>使用凭证下载指定文件</h2>
|
||||
* <p>根据请求所持凭证,下载指定文件资源。</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
* @param request javax.servlet.http.HttpServletRequest 请求对象
|
||||
* @param response javax.servlet.http.HttpServletResponse 响应对象
|
||||
*/
|
||||
void downloadFileByKey(HttpServletRequest request,HttpServletResponse response);
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package kohgylw.kiftd.server.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import kohgylw.kiftd.server.enumeration.AccountAuth;
|
||||
import kohgylw.kiftd.server.mapper.NodeMapper;
|
||||
import kohgylw.kiftd.server.model.Node;
|
||||
import kohgylw.kiftd.server.service.ExternalDownloadService;
|
||||
import kohgylw.kiftd.server.util.ConfigureReader;
|
||||
import kohgylw.kiftd.server.util.FileBlockUtil;
|
||||
import kohgylw.kiftd.server.util.LogUtil;
|
||||
import kohgylw.kiftd.server.util.RangeFileStreamWriter;
|
||||
|
||||
@Service
|
||||
public class ExternalDownloadServiceImpl extends RangeFileStreamWriter implements ExternalDownloadService {
|
||||
|
||||
private static Map<String, String> downloadKeyMap = new HashMap<>();// 凭证池,用于存储生成好的下载凭证
|
||||
private static final String CONTENT_TYPE = "application/octet-stream";
|
||||
|
||||
@Resource
|
||||
private NodeMapper nm;
|
||||
@Resource
|
||||
private LogUtil lu;
|
||||
@Resource
|
||||
private FileBlockUtil fbu;
|
||||
|
||||
@Override
|
||||
public String getDownloadKey(HttpServletRequest request) {
|
||||
// 首先进行权限检查
|
||||
final String account = (String) request.getSession().getAttribute("ACCOUNT");
|
||||
// 权限检查
|
||||
if (ConfigureReader.instance().authorized(account, AccountAuth.DOWNLOAD_FILES)) {
|
||||
// 找到要下载的文件节点
|
||||
final String fileId = request.getParameter("fId");
|
||||
if (fileId != null) {
|
||||
final Node f = this.nm.queryById(fileId);
|
||||
if (f != null) {
|
||||
// 获取凭证
|
||||
synchronized (downloadKeyMap) {
|
||||
// 查找该资源是否已经生成了一个凭证,如有,则直接使用,否则,新生成一个加入到凭证表。
|
||||
this.lu.writeShareFileURLEvent(request, f);
|
||||
if (downloadKeyMap.containsValue(f.getFileId())) {
|
||||
Entry<String, String> k = downloadKeyMap.entrySet().parallelStream()
|
||||
.filter((e) -> e.getValue().equals(f.getFileId())).findFirst().get();
|
||||
return k.getKey();
|
||||
} else {
|
||||
String dKey = UUID.randomUUID().toString();
|
||||
downloadKeyMap.put(dKey, f.getFileId());
|
||||
return dKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadFileByKey(HttpServletRequest request, HttpServletResponse response) {
|
||||
final String dkey = request.getParameter("dkey");
|
||||
// 权限凭证有效性并确认其对应的资源
|
||||
if (dkey != null) {
|
||||
// 找到要下载的文件节点
|
||||
String fId = null;
|
||||
synchronized (downloadKeyMap) {
|
||||
fId = downloadKeyMap.get(dkey);
|
||||
}
|
||||
if (fId != null) {
|
||||
Node f = this.nm.queryById(fId);
|
||||
if (f != null) {
|
||||
File target = this.fbu.getFileFromBlocks(f);
|
||||
if (target != null && target.isFile()) {
|
||||
writeRangeFileStream(request, response, target, f.getFileName(), CONTENT_TYPE);
|
||||
if (request.getHeader("Range") == null) {
|
||||
this.lu.writeDownloadFileByKeyEvent(f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
// 处理无法下载的资源
|
||||
response.sendError(404);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -36,6 +36,7 @@ public class PlayVideoServiceImpl implements PlayVideoService {
|
||||
final String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
switch (suffix) {
|
||||
case "mp4":
|
||||
case "mov":
|
||||
// 对于mp4后缀的视频,进一步检查其编码是否为h264,如果是,则设定无需转码直接播放
|
||||
MultimediaObject mo = new MultimediaObject(fbu.getFileFromBlocks(f));
|
||||
try {
|
||||
@ -50,7 +51,6 @@ public class PlayVideoServiceImpl implements PlayVideoService {
|
||||
vi.setNeedEncode("Y");
|
||||
return vi;
|
||||
case "webm":
|
||||
case "mov":
|
||||
case "avi":
|
||||
case "wmv":
|
||||
case "mkv":
|
||||
|
@ -215,6 +215,59 @@ public class LogUtil {
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>简述</h2>
|
||||
* <p>详细描述</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
* @param xxx 参数描述
|
||||
* @return xxx
|
||||
*/
|
||||
public void writeDownloadFileByKeyEvent(Node f) {
|
||||
if (ConfigureReader.instance().inspectLogLevel(LogLevel.Event)) {
|
||||
Thread t = new Thread(() -> {
|
||||
Folder folder = fm.queryById(f.getFileParentFolder());
|
||||
List<Folder> l = fu.getParentList(folder.getFolderId());
|
||||
String pl = new String();
|
||||
for (Folder i : l) {
|
||||
pl = pl + i.getFolderName() + "/";
|
||||
}
|
||||
String content = ">OPERATE [Download file By Shared URL]\r\n>PATH [" + pl
|
||||
+ folder.getFolderName() + "]\r\n>NAME [" + f.getFileName() + "]";
|
||||
writeToLog("Event", content);
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* <h2>记录分享下载链接事件</h2>
|
||||
* <p>当用户试图获取一个资源的下载链接时,记录此事件。</p>
|
||||
* @author 青阳龙野(kohgylw)
|
||||
*/
|
||||
public void writeShareFileURLEvent(HttpServletRequest request, Node f) {
|
||||
if (ConfigureReader.instance().inspectLogLevel(LogLevel.Event)) {
|
||||
String account = (String) request.getSession().getAttribute("ACCOUNT");
|
||||
if (account == null || account.length() == 0) {
|
||||
account = "Anonymous";
|
||||
}
|
||||
String a = account;
|
||||
Thread t = new Thread(() -> {
|
||||
Folder folder = fm.queryById(f.getFileParentFolder());
|
||||
List<Folder> l = fu.getParentList(folder.getFolderId());
|
||||
String pl = new String();
|
||||
for (Folder i : l) {
|
||||
pl = pl + i.getFolderName() + "/";
|
||||
}
|
||||
String content = ">ACCOUNT [" + a + "]\r\n>OPERATE [Share Download file URL]\r\n>PATH [" + pl
|
||||
+ folder.getFolderName() + "]\r\n>NAME [" + f.getFileName() + "]";
|
||||
writeToLog("Event", content);
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 以格式化记录重命名文件日志
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Generated by Maven Integration for Eclipse
|
||||
#Tue Apr 02 17:19:43 CST 2019
|
||||
#Wed Apr 03 11:57:11 CST 2019
|
||||
version=1.0.17-SNAPSHOT
|
||||
groupId=kohgylw
|
||||
m2e.projectName=kiftd
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -91,8 +91,12 @@ body {
|
||||
width: 150px;
|
||||
height: 35px;
|
||||
background-color: #FCFCFC;
|
||||
color: #104E8B;
|
||||
alpha (Opacity=70);
|
||||
color: #104E8B; alpha (Opacity=70);
|
||||
-moz-opacity: 0.7;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
word-break: break-all;
|
||||
word-wrap: break-all;
|
||||
}
|
||||
|
2
webContext/css/overrall.min.css
vendored
2
webContext/css/overrall.min.css
vendored
@ -1 +1 @@
|
||||
@charset "UTF-8";.titlebox{height:30px;line-height:30px;padding-top:5px;padding-bottom:5px;padding-left:10px;padding-right:10px;position:relative;bottom:-7px}.titletext{color:#000;font-size:22px}.graytext{color:#8b8b8b}.subtitle{color:#5cacee;text-align:center}.rightbtn{border-right:10px;float:right;margin-left:10px}.heading{margin-top:-10px;margin-bottom:-10px}.centerText{text-align:center}.uploadstatusbox{color:#9c9c9c;height:48px;overflow:auto}html{height:100%}body{height:100%}.loading{line-height:56px;color:#545454;padding-left:60px;font-size:15px;background:#fff url(loading.gif) no-repeat 10px 50%;opacity:.7;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;filter:alpha(opacity=70)}.wordbreak{word-break:break-all}.filetableheaderstyle{height:40px;float:left;height:100%;margin-top:15px;overflow:hidden}.gobacktopbox{width:100%;height:30px;position:fixed;top:0;left:0}.gobacktopbutton{width:150px;height:35px;background-color:#fcfcfc;color:#104e8b;alpha(Opacity=70);-moz-opacity:.7;opacity:.7}
|
||||
@charset "UTF-8";.titlebox{height:30px;line-height:30px;padding-top:5px;padding-bottom:5px;padding-left:10px;padding-right:10px;position:relative;bottom:-7px}.titletext{color:#000;font-size:22px}.graytext{color:#8b8b8b}.subtitle{color:#5cacee;text-align:center}.rightbtn{border-right:10px;float:right;margin-left:10px}.heading{margin-top:-10px;margin-bottom:-10px}.centerText{text-align:center}.uploadstatusbox{color:#9c9c9c;height:48px;overflow:auto}html{height:100%}body{height:100%}.loading{line-height:56px;color:#545454;padding-left:60px;font-size:15px;background:#fff url(loading.gif) no-repeat 10px 50%;opacity:.7;-moz-border-radius:20px;-webkit-border-radius:20px;border-radius:20px;filter:alpha(opacity=70)}.wordbreak{word-break:break-all}.filetableheaderstyle{height:40px;float:left;height:100%;margin-top:15px;overflow:hidden}.gobacktopbox{width:100%;height:30px;position:fixed;top:0;left:0}.gobacktopbutton{width:150px;height:35px;background-color:#fcfcfc;color:#104e8b;alpha(Opacity=70);-moz-opacity:.7;opacity:.7}td{word-break:break-all;word-wrap:break-all}
|
@ -448,7 +448,7 @@
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h5 id="deleteFileMessage"></h5>
|
||||
<h5 id="deleteFileMessage" class="wordbreak"></h5>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
|
||||
|
@ -302,6 +302,11 @@ $(function() {
|
||||
$('#gobacktotopbox').addClass("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// 打开查看下载链接时,向后台生成/获取下载链接
|
||||
$('#downloadURLCollapse').on('shown.bs.collapse', function () {
|
||||
getDownloadURL();
|
||||
});
|
||||
});
|
||||
|
||||
// 根据屏幕大小增删表格显示内容
|
||||
@ -1324,14 +1329,15 @@ function showUploadFileAlert(txt) {
|
||||
|
||||
// 显示下载文件模态框
|
||||
function showDownloadModel(fileId, fileName) {
|
||||
$("#downloadModal").modal('toggle');
|
||||
$("#downloadFileName").text("提示:您确认要下载文件:[" + fileName + "]么?");
|
||||
$("#downloadHrefBox").html("<a href='"+window.location.protocol+"//"+window.location.host+"/homeController/downloadFile.do?fileId="+fileId+"'>"+window.location.protocol+"//"+window.location.host+"/homeController/downloadFile.do?fileId="+fileId+"</a>");
|
||||
$("#downloadHrefBox").html("<span class='text-muted'>正在生成...</span>");
|
||||
getDownloadFileId=fileId;
|
||||
$("#downloadFileBox")
|
||||
.html(
|
||||
"<button id='dlmbutton' type='button' class='btn btn-primary' onclick='dodownload("
|
||||
+ '"' + fileId + '"' + ")'>开始下载</button>");
|
||||
$("#dlmbutton").attr('disabled', false);
|
||||
$("#downloadModal").modal('show');
|
||||
}
|
||||
|
||||
// 执行下载操作
|
||||
@ -2216,6 +2222,27 @@ function doSearchFile(){
|
||||
endLoading();
|
||||
}
|
||||
|
||||
// 返回顶部实现
|
||||
function goBackToTop(){
|
||||
$('html,body').animate({scrollTop: 0},'slow');
|
||||
}
|
||||
|
||||
var getDownloadFileId;
|
||||
|
||||
// 获取某一文件的下载链接
|
||||
function getDownloadURL(){
|
||||
$.ajax({
|
||||
url:'externalLinksController/getDownloadKey.ajax',
|
||||
type:'POST',
|
||||
dataType:'text',
|
||||
data:{
|
||||
fId:getDownloadFileId
|
||||
},
|
||||
success:function(result){
|
||||
$("#downloadHrefBox").html("<a href='"+window.location.protocol+"//"+window.location.host+"/externalLinksController/downloadFileByKey.do?dkey="+result+"'>"+window.location.protocol+"//"+window.location.host+"/externalLinksController/downloadFileByKey.do?dkey="+result+"</a>");
|
||||
},
|
||||
error:function(){
|
||||
$("#downloadHrefBox").html("<span class='text-muted'>获取失败,请检查网络状态或<a href='javascript:void(0);' onclick='getDownloadURL()'>点此</a>重新获取。</span>");
|
||||
}
|
||||
});
|
||||
}
|
4
webContext/js/home.min.js
vendored
4
webContext/js/home.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user