Joyber 发布的文章

文档
https://kkfileview.keking.cn/zh-cn/docs/home.html

应用例子:

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-base64@3.6.0/base64.min.js"></script>

var previewUrl = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl)));

部署运行(摘自官方文档) #
1). 物理机或虚拟机上运行 #
从 码云发行版本 下载最新版发行包
解压kkFileView-x.x.x文件(Windows用.zip包,Linux/MacOS用.tar.gz包)
打开解压后文件夹的bin目录,运行startup脚本(Windows下以管理员身份运行startup.bat,Linux以root用户运行startup.sh)
浏览器访问本机8012端口 http://127.0.0.1:8012 即可看到项目演示用首页
2). Docker容器环境环境运行 #
拉取镜像
docker pull keking/kkfileview
运行
docker run -it -p 8012:8012 keking/kkfileview
浏览器访问容器8012端口 http://xxx.xxx.xxx.xxx:8012 即可看到项目演示用首页

实践的时候通过在虚拟机上采用1方式没法有成功运行起来,推荐采用2方式,在docker环境中使用
创建容器命令

docker run -dit -p 8012:8012 -v /www/kkfileivew/fonts:/usr/share/fonts/ -e KK_OFFICE_PREVIEW_TYPE="pdf" -e BASE_URL="https://www.xxx.com/preview" -e KK_CONTEXT_PATH="/preview/" --restart=always --name kkfileview 172.19.0.14:5000/keking/kkfileview:latest

用了私有仓库,8012端口映射,出现乱码或者中文问题(常见问题中有说明),映射了/usr/share/fonts/目录到母机上,方便管理字体,使用了默认的pdf预览方式,应用了nginx反向代理功能

nginx反向代理配置的时候,需要配置一下内容替换,前台是https访问,后端是走的http反代,所以页面中会有http的链接,并且进了端口,所以替换之

location ~ /preview/ {
    proxy_pass http://172.19.0.14:8012;
    proxy_set_header Host www.xxx.com;
    
    sub_filter "http://www.xxx.com:80" "https://www.xxx.com";
    sub_filter_once off;
}

项目中就是这个地址可以预览文件了
https://www.xxx.com/preview/onlinePreview?url='+encodeURIComponent(Base64.encode(previewUrl)));

实际线上项目需要考虑一些安全问题,应该限制一些文件类别,限制文件来源等等。

常见问题:https://kkfileview.keking.cn/zh-cn/docs/faq.html

配置文档:https://kkfileview.keking.cn/zh-cn/docs/config.html
默认配置文件(4.0版)

#######################################不可动态配置,需要重启生效#######################################
server.port = ${KK_SERVER_PORT:8012}
server.servlet.context-path= ${KK_CONTEXT_PATH:/}
server.servlet.encoding.charset = utf-8
#文件上传限制
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB
## Freemarker 配置
spring.freemarker.template-loader-path = classpath:/web/
spring.freemarker.cache = false
spring.freemarker.charset = UTF-8
spring.freemarker.check-template-location = true
spring.freemarker.content-type = text/html
spring.freemarker.expose-request-attributes = true
spring.freemarker.expose-session-attributes = true
spring.freemarker.request-context-attribute = request
spring.freemarker.suffix = .ftl

# office-plugin
## office转换服务的进程数,默认开启两个进程
office.plugin.server.ports = 2001,2002
## office 转换服务 task 超时时间,默认五分钟
office.plugin.task.timeout = 5m

#文件资源路径(默认为打包根路径下的file目录下)
#file.dir = D:\\kkFileview\\
file.dir = ${KK_FILE_DIR:default}
#openoffice home路径
#office.home = C:\\Program Files (x86)\\OpenOffice 4
office.home = ${KK_OFFICE_HOME:default}

#缓存实现类型,不配默认为内嵌RocksDB(type = default)实现,可配置为redis(type = redis)实现(需要配置spring.redisson.address等参数)和 JDK 内置对象实现(type = jdk),
cache.type =  ${KK_CACHE_TYPE:jdk}
#redis连接,只有当cache.type = redis时才有用
spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:127.0.0.1:6379}
spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:}
#缓存是否自动清理 true 为开启,注释掉或其他值都为关闭
cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
#缓存自动清理时间,cache.clean.enabled = true时才有用,cron表达式,基于Quartz cron
cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}

#######################################可在运行时动态配置#######################################
#提供预览服务的地址,默认从请求url读,如果使用nginx等反向代理,需要手动设置
#base.url = https://file.keking.cn
base.url = ${KK_BASE_URL:default}

#信任站点,多个用','隔开,设置了之后,会限制只能预览来自信任站点列表的文件,默认不限制
#trust.host = file.keking.cn,kkfileview.keking.cn
trust.host = ${KK_TRUST_HOST:default}

#是否启用缓存
cache.enabled = ${KK_CACHE_ENABLED:true}

#文本类型,默认如下,可自定义添加
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}
#多媒体类型,默认如下,可自定义添加
media = ${KK_MEDIA:mp3,wav,mp4,flv}
#是否开启多媒体类型转视频格式转换,目前可转换视频格式有:avi,mov,wmv,3gp,rm
#请谨慎开启此功能,建议异步调用添加到处理队列,并且增加任务队列处理线程,防止视频转换占用完线程资源,转换比较耗费时间,并且控制了只能串行处理转换任务
media.convert.disable = ${KK_MEDIA_CONVERT_DISABLE:false}
#支持转换的视频类型
convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm}
#office类型文档(word ppt)样式,默认为图片(image),可配置为pdf(预览时也有按钮切换)
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#是否关闭office预览切换开关,默认为false,可配置为true关闭
office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}

#是否禁止下载转换生成的pdf文件
pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
#是否禁用首页文件上传
file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false}

#预览源为FTP时 FTP用户名,可在ftp url后面加参数ftp.username=ftpuser指定,不指定默认用配置的
ftp.username = ${KK_FTP_USERNAME:ftpuser}
#预览源为FTP时 FTP密码,可在ftp url后面加参数ftp.password=123456指定,不指定默认用配置的
ftp.password = ${KK_FTP_PASSWORD:123456}
#预览源为FTP时, FTP连接默认ControlEncoding(根据FTP服务器操作系统选择,Linux一般为UTF-8,Windows一般为GBK),可在ftp url后面加参数ftp.control.encoding=UTF-8指定,不指定默认用配置的
ftp.control.encoding = ${KK_FTP_CONTROL_ENCODING:UTF-8}

#水印内容
#例:watermark.txt = ${WATERMARK_TXT:凯京科技内部文件,严禁外泄}
#如需取消水印,内容设置为空即可,例:watermark.txt = ${WATERMARK_TXT:}
watermark.txt = ${WATERMARK_TXT:}
#水印x轴间隔
watermark.x.space = ${WATERMARK_X_SPACE:10}
#水印y轴间隔
watermark.y.space = ${WATERMARK_Y_SPACE:10}
#水印字体
watermark.font = ${WATERMARK_FONT:微软雅黑}
#水印字体大小
watermark.fontsize = ${WATERMARK_FONTSIZE:18px}
#水印字体颜色
watermark.color = ${WATERMARK_COLOR:black}
#水印透明度,要求设置在大于等于0.005,小于1
watermark.alpha = ${WATERMARK_ALPHA:0.2}
#水印宽度
watermark.width = ${WATERMARK_WIDTH:180}
#水印高度
watermark.height = ${WATERMARK_HEIGHT:80}
#水印倾斜度数,要求设置在大于等于0,小于90
watermark.angle = ${WATERMARK_ANGLE:10}

ffmpeg推流时报错 Unknown encoder 'libx264'
https://blog.csdn.net/yuxielea/article/details/103146362

FFmpeg常用推流命令
https://www.jianshu.com/p/d541b317f71c

nginx-rtmp-module 模块
https://github.com/arut/nginx-rtmp-module

FFmpeg
https://github.com/FFmpeg/FFmpeg

vlc流媒体播放器 测试拉流工具
https://get.videolan.org/vlc/3.0.16/win32/vlc-3.0.16-win32.exe
拉流地址:rtmp://127.0.0.1:1935/rtmplive/home

将mp4推流到服务端口
ffmpeg -re -i /root/test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://127.0.0.1:1935/rtmplive/home

ffmpeg 编译问题集锦
RPM 资源 libpostproc.so.55(LIBPOSTPROC_55)(64bit)
http://rpmfind.net/linux/rpm2html/search.php?query=libpostproc.so.55(LIBPOSTPROC_55)(64bit)

查看下安装目录下有没有这些库文件

export LD_LIBRARY_PATH=/usr/local/lib/:/root/FFmpeg-n4.4/libpostproc:/root/FFmpeg-n4.4/libswresample:/root/FFmpeg-n4.4/libswscale:/root/FFmpeg-n4.4/libavutil

1、matroskadec.c:843: undefined reference to ‘ff_log2_tab’
原因:ff_log2_tab变量定义在log2_tab.c文件中,libavformat模块有引用到,需要包含
处理:在libavformat/matroskadec.c文件开头加上 ==》 #include "libavutil/log2_tab.c"

2、asvdec.c:74: undefined reference to ‘ff_reverse’
原因:同上
处理:在libavcodec/asvdec.c文件开头加上 ==> #include "libavutil/reverse.c"

解决ffmpeg执行报错“ffmpeg: error while loading shared libraries: libavdevice.so.58: cannot open shared object file: No such file or directory”的问题
https://www.cnblogs.com/comexchan/p/12079333.html

语法: sendfile on | off;
默认值: sendfile off;
上下文: http,server,location,if in location
}

指定是否使用sendfile系统调用来传输文件。
sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝。
参考文章:https://www.jianshu.com/p/70e1c396c320

Syntax:    tcp_nopush on | off;
Default:    tcp_nopush off;
Context:    http, server, location

TCP_NOPUSH 是 FreeBSD 的一个 socket 选项,对应 Linux 的 TCP_CORK,Nginx 里统一用 tcp_nopush 来控制它,并且只有在启用了 sendfile 之后才生效。启用它之后,数据包会累计到一定大小之后才会发送,减小了额外开销,提高网络效率。

Syntax:    tcp_nodelay on | off;
Default:    tcp_nodelay on;
Context:    http, server, location

TCP_NODELAY 也是一个 socket 选项,启用后会禁用 Nagle 算法,尽快发送数据,某些情况下可以节约 200ms(Nagle 算法原理是:在发出去的数据还未被确认之前,新生成的小数据先存起来,凑满一个 MSS 或者等到收到确认后再发送)。Nginx 只会针对处于 keep-alive 状态的 TCP 连接才会启用 tcp_nodelay。当连接转换为keep-alive状态时,启用该选项。此外,它在SSL连接上启用,用于无缓冲代理和WebSocket代理。

可以看到 TCP_NOPUSH 是要等数据包累积到一定大小才发送,TCP_NODELAY 是要尽快发送,二者相互矛盾。实际上,它们确实可以一起用,最终的效果是先填满包,再尽快发送。
官方文档参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#tcp_nodelay

Syntax:    keepalive_timeout timeout [header_timeout];
Default:    keepalive_timeout 75s;
Context:    http, server, location

第一个参数设置一个超时,在这个超时期间,保持活动的客户端连接将在服务器端保持打开。0值禁用保持连接的客户端连接。第二个可选参数在Keep-Alive: timeout=time响应报头字段中设置一个值。两个参数可能不同。Keep-Alive: timeout=time报头字段被Mozilla和Konqueror识别。MSIE会在大约60秒内自行关闭保持连接。

keepalive_timeout 60;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

fastcgi模块参数

gzip on;
gzip_min_length  1k;
gzip_buffers     4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types     text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on;
gzip_proxied   expired no-cache no-store private auth;
gzip_disable   "MSIE [1-6]\.";

开启gzip压缩

server_tokens off;

启用或禁用在错误页面和服务器响应报头字段中发出nginx版本。build参数(1.11.10)允许随nginx版本一起发出build名称。此外,作为我们商业订阅的一部分,从版本1.9.13开始,错误页面上的签名和服务器响应头字段值可以使用变量字符串显式设置。空字符串禁止发送Server字段。

map $http_x_forwarded_for  $clientRealIp {
  ""      $remote_addr;
  ~^(?P<firstAddr>[0-9\.]+),?.*$  $firstAddr;
}

过CDN,获取真实客户端IP,$clientRealIp

====
其它待补充

网站必须是https协议访问才有桌面通知的权限,http直接返回denied,并且移动端全面未支持桌面通知

<script type="javascript">

  // 先检查浏览器是否支持
  if (!window.Notification) {
    console.log('浏览器不支持通知');
  } else {
    // 检查用户曾经是否同意接受通知
    if (Notification.permission === 'granted') {
      var notification = new Notification(title, options); // 显示通知
    } else if (Notification.permission === 'default') {
      // 用户还未选择,可以询问用户是否同意发送通知
      Notification.requestPermission().then(permission => {
        if (permission === 'granted') {
          console.log('用户同意授权');
          var notification = new Notification(title, options); // 显示通知
        } else if (permission === 'default') {
          console.warn('用户关闭授权 未刷新页面之前 可以再次请求授权');
        } else {
          // denied
          console.log('用户拒绝授权 不能显示通知');
        }
      });
    } else {
      // denied 用户拒绝
      console.log('用户曾经拒绝显示通知');
    }

  if (Notification.permission === 'granted') {
    console.log('用户曾经同意授权');
     // 随时可以显示通知
  } else if (Notification.permission === 'default') {
    console.log('用户还未选择同意/拒绝');
    // 下一步请求用户授权
  } else {
    console.log('用户曾经拒绝授权 不能显示通知');
  }


    var option = {
        dir: 'rtl', //主体内容的水平书写顺序
        body: '通知的内容<b>html b code</b>',  //不支持html标签,实测win10长度能够显示4行文字内容,1行17个汉字
        tag: 'tag1',
        icon: 'https://maill.xxxx.com/common/images/Logo_50_0925.png', //正方形图片的地址
        data: {col1: 'test', col2: 'hello'},
        renotify: true,
        requireInteraction: true,
        silent: true, //是否无声
        sound:'',
        noscreen: false,
    }
    var notify = new Notification('我的通知的标题', option)
    notify.onshow = function(){console.log(arguments)}
    notify.onerror = function(){console.log(arguments)}
    notify.onclose = function(){console.log(arguments)}
    notify.onclick = function(){console.log(arguments);}
</script>

参数说明(摘自张鑫旭老师的博客):https://www.zhangxinxu.com/wordpress/2016/07/know-html5-web-notification/comment-page-2/#comment-414810
dir 默认值是auto, 可以是ltr或rtl,有点类似direction属性。表示提示主体内容的水平书写顺序。
lang 提示的语言。
body 提示主体内容。字符串。会在标题的下面显示
tag 字符串。标记当前通知的标签。
icon 字符串。通知面板左侧那个图标地址。
data 任意类型和通知相关联的数据。
vibrate 通知显示时候,设备震动硬件需要的振动模式。所谓振动模式,指的是一个描述交替时间的数组,分别表示振动和不振动的毫秒数,一直交替下去。例如[200, 100, 200]表示设备振动200毫秒,然后停止100毫秒,再振动200毫秒。
renotify 布尔值。新通知出现的时候是否替换之前的。如果设为true,则表示替换,相同tag属性的只会显示一个,而不会是默认的叠高楼:叠高楼
silent 布尔值。通知出现的时候,是否无声。
sound 字符串。音频地址。表示通知出现要播放的声音资源。
noscreen 布尔值。是否不再屏幕上显示通知信息。
sticky 布尔值。是否通知具有粘性,这样用户不太容易清除通知。

当浏览器最小化后,点击通知让浏览器恢复显示的方法,比如聊天页面有消息后,让用户回到浏览器

    notify.onclick = function () {
        var win = window.open();
        win.close();
    }

有时候想监测浏览器当前标签的状态,以选择使用何种通知方式(页面通知、桌面通知、浏览器标题栏提示),如下方法可以监测页面的变化

//浏览器监测标签状态,离开标签,最大最小化,都会触发这个事件,建议亲自试试以下代码
document.addEventListener("visibilitychange", function() {
  console.log( document.visibilityState );
  console.log(document.hidden);
});
//我们只需要判断  document.visibilityState  或者 document.hidden 即可知道页面是隐藏的还是显示的

在必要的时候,还有一招就是浏览器标题栏提示

//实际测试,在浏览器最小化或者标签非活动标签(切换到其它标签)时,交换时间间隔变长,或者失效
var titleInit = document.title
var isShine = true //是否提示状态
setInterval(function() {
    var title = document.title;
    if (isShine == true) {
        if (/新消息/.test(title) == false) {
            document.title = '【你有新消息】';    
        } else {
            document.title = '【     】';
        }
    } else {
        document.title = titleInit;
    }
}, 500);

notification其他细节: 摘自其它技术文章,有可能过时,仅作参考
用户拒绝显示通知:
一旦用户禁止网站显示通知,网站就不能再请求用户授权显示通知,需要用户去设置中更改。
chrome浏览器的通知设置位置:设置>高级>内容设置>通知
saafari浏览器:偏好设置>网站>通知>找到网站>修改权限/恢复默认

关闭请求权限:
在chorme浏览器中:当用户关闭请求权限的弹窗(右上角的叉叉),页面还没刷新,我们可以再次向用户请求权限。页面刷新过后,浏览器默认用户拒绝。
在safari浏览器下,没有关闭请求权限的选项,用户必须选择同意/拒绝。

icon不显示问题:
可能是网片有防盗链
safari下面不能显示icon
在safari下面,同一个网站(比如谷歌),同样的代码,chorme可以正常显示icon,safari却没有icon,也没有报错。
在stack overflow里面看到safari只支持body和tag选项,并不支持icon选项。

tag:
tag相同的通知,同时只能出现一个,老通知是否会被覆盖取决于:renotify配置和浏览器。
chrome下:当通知关闭之后,上次出现过的tag在一段时间内,不能再出现,比如刷新页面再请求相同tag的通知。(在safari下正常出现)

连续触发
在safari和chrome下短时间内连续触发通知(不设tag,不设requireInteraction),会出现如下表现:
合并为一条通知,内容只提示有XXX条通知
这个表现,通知没有icon、标题、内容,就显得没有意义了,浏览器以这种形式,限制开发者不要频繁打扰用户。