├── app.ico
├── dist
└── qrfs.7z
├── screenshots
├── phone.jpg
└── qrfs2.jpg
├── .editorconfig
├── .gitignore
├── qrfs.aproj
├── LICENSE
├── README.md
├── include
├── filepond.min.css
└── filepond.min.js
└── main.aardio
/app.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aardio/qrfs/HEAD/app.ico
--------------------------------------------------------------------------------
/dist/qrfs.7z:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aardio/qrfs/HEAD/dist/qrfs.7z
--------------------------------------------------------------------------------
/screenshots/phone.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aardio/qrfs/HEAD/screenshots/phone.jpg
--------------------------------------------------------------------------------
/screenshots/qrfs2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aardio/qrfs/HEAD/screenshots/qrfs2.jpg
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 |
6 | [*.{aardio,table,aau}]
7 | end_of_line = crlf
8 | charset = utf-8
9 | indent_style = tab
10 | indent_size = 4
11 |
12 | [*.{aau}]
13 | charset = utf-8-bom
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.build/qrfs.main.aardio
2 | /.build/qrfs.init.aardio
3 | /.build/qrfs.Manifest.xml
4 | /.build/update-maker.table
5 | /.build/update-maker.aardio
6 | /.update-files/
7 | .build/default.init.aardio
8 | .build/mp.lock
9 | /.imtip
10 |
--------------------------------------------------------------------------------
/qrfs.aproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 aardio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qrfs - 扫码快传
2 | 内网扫码共享文件与剪贴板,不走公网速度更快更安全。
3 |
4 | [下载](dist/qrfs.7z) 仅 726 KB。软件非常小,简单方便,传输大文件快如闪电。软件兼容 XP,Vista,Win7,Win8,Win10,Win11 等所有桌面操作系统。
5 |
6 | 
7 |
8 | 在电脑上运行后,只要把文件或目录拖拽到窗口上就可以共享文件。全局域网都可以通过网页访问,手机扫码就可以打开网页端。
9 |
10 | **不需要在手机上安装任何客户端!**
11 |
12 | - 文件上传目录为网站根目录下的 `/upload/` 目录 。
13 | - 手机可在扫码打开的主页点击【共享剪贴板】,也可以选择扫码直接打开【共享剪贴板】,手机端可同步显示电脑端的剪贴板文本,在手机端修改文本也会同步更新电脑剪贴板( 双向同步 )。
14 |
15 | 
16 |
17 | 此软件基于 [aardio](https://www.aardio.com) 标准库中的 wsock.tcp.asynHttpServer - 由 纯 aardio 代码实现的单线程异步 HTTP 服务端,体积仅数十 KB,可运行 aardio 开发的网站。支持高速上传下载、断点续传、304 缓存、分块传输、Keep Alive。支持共享端口的 HTTP / WebSocket 双服务端( 由标准库 web.socket.server 实现)。
18 |
19 | aardio 标准库中有以下几个 HTTP 服务端库
20 | - wsock.tcp.asynHttpServer 单线程异步服务端。
21 | - wsock.tcp.simpleHttpServer 多线程服务端
22 |
23 | 这几个 HTTP 服务端都是由纯 aardio 代码实现,可以方便地在桌面软件中实现嵌入式 HTTP 服务端,支持随机端口,可生成独立 EXE 文件,无外部依赖。
24 |
25 | aardio 标准库 fastcgi.client 则可以创建支持 IIS 服务器环境的 FastCGI 服务端,。
26 |
27 | fastcgi.client , wsock.tcp.asynHttpServer , wsock.tcp.simpleHttpServer 都支持完全相同的网站开发接口。请参考文档:[aardio 语言 Web 服务端开发指南](https://www.aardio.com/zh-cn/doc/guide/quickstart/web-server.html)
--------------------------------------------------------------------------------
/include/filepond.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * FilePond 4.28.2
3 | * Licensed under MIT, https://opensource.org/licenses/MIT/
4 | * Please visit https://pqina.nl/filepond/ for details.
5 | */
6 |
7 | /* eslint-disable */
8 | .filepond--assistant{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--browser.filepond--browser{position:absolute;margin:0;padding:0;left:1em;top:1.75em;width:calc(100% - 2em);opacity:0;font-size:0}.filepond--data{position:absolute;width:0;height:0;padding:0;margin:0;border:none;visibility:hidden;pointer-events:none;contain:strict}.filepond--drip{position:absolute;top:0;left:0;right:0;bottom:0;overflow:hidden;opacity:.1;pointer-events:none;border-radius:.5em;background:rgba(0,0,0,.01)}.filepond--drip-blob{-webkit-transform-origin:center center;transform-origin:center center;width:8em;height:8em;margin-left:-4em;margin-top:-4em;background:#292625;border-radius:50%}.filepond--drip-blob,.filepond--drop-label{position:absolute;top:0;left:0;will-change:transform,opacity}.filepond--drop-label{right:0;margin:0;color:#4f4f4f;display:flex;justify-content:center;align-items:center;height:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--drop-label.filepond--drop-label label{display:block;margin:0;padding:.5em}.filepond--drop-label label{cursor:default;font-size:.875em;font-weight:400;text-align:center;line-height:1.5}.filepond--label-action{text-decoration:underline;-webkit-text-decoration-skip:ink;text-decoration-skip-ink:auto;-webkit-text-decoration-color:#a7a4a4;text-decoration-color:#a7a4a4;cursor:pointer}.filepond--root[data-disabled] .filepond--drop-label label{opacity:.5}.filepond--file-action-button.filepond--file-action-button{font-size:1em;width:1.625em;height:1.625em;font-family:inherit;line-height:inherit;margin:0;padding:0;border:none;outline:none;will-change:transform,opacity}.filepond--file-action-button.filepond--file-action-button span{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--file-action-button.filepond--file-action-button svg{width:100%;height:100%}.filepond--file-action-button.filepond--file-action-button:after{position:absolute;left:-.75em;right:-.75em;top:-.75em;bottom:-.75em;content:""}.filepond--file-action-button{cursor:auto;color:#fff;border-radius:50%;background-color:rgba(0,0,0,.5);background-image:none;box-shadow:0 0 0 0 hsla(0,0%,100%,0);transition:box-shadow .25s ease-in}.filepond--file-action-button:focus,.filepond--file-action-button:hover{box-shadow:0 0 0 .125em hsla(0,0%,100%,.9)}.filepond--file-action-button[disabled]{color:hsla(0,0%,100%,.5);background-color:rgba(0,0,0,.25)}.filepond--file-action-button[hidden]{display:none}.filepond--action-edit-item.filepond--action-edit-item{width:2em;height:2em;padding:.1875em}.filepond--action-edit-item.filepond--action-edit-item[data-align*=center]{margin-left:-.1875em}.filepond--action-edit-item.filepond--action-edit-item[data-align*=bottom]{margin-bottom:-.1875em}.filepond--action-edit-item-alt{border:none;line-height:inherit;background:transparent;font-family:inherit;color:inherit;outline:none;padding:0;margin:0 0 0 .25em;pointer-events:all;position:absolute}.filepond--action-edit-item-alt svg{width:1.3125em;height:1.3125em}.filepond--action-edit-item-alt span{font-size:0;opacity:0}.filepond--file-info{position:static;display:flex;flex-direction:column;align-items:flex-start;flex:1;margin:0 .5em 0 0;min-width:0;will-change:transform,opacity;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--file-info *{margin:0}.filepond--file-info .filepond--file-info-main{font-size:.75em;line-height:1.2;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;width:100%}.filepond--file-info .filepond--file-info-sub{font-size:.625em;opacity:.5;transition:opacity .25s ease-in-out;white-space:nowrap}.filepond--file-info .filepond--file-info-sub:empty{display:none}.filepond--file-status{position:static;display:flex;flex-direction:column;align-items:flex-end;flex-grow:0;flex-shrink:0;margin:0;min-width:2.25em;text-align:right;will-change:transform,opacity;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.filepond--file-status *{margin:0;white-space:nowrap}.filepond--file-status .filepond--file-status-main{font-size:.75em;line-height:1.2}.filepond--file-status .filepond--file-status-sub{font-size:.625em;opacity:.5;transition:opacity .25s ease-in-out}.filepond--file-wrapper.filepond--file-wrapper{border:none;margin:0;padding:0;min-width:0;height:100%}.filepond--file-wrapper.filepond--file-wrapper>legend{position:absolute;overflow:hidden;height:1px;width:1px;padding:0;border:0;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(50%);clip-path:inset(50%);white-space:nowrap}.filepond--file{position:static;display:flex;height:100%;align-items:flex-start;padding:.5625em;color:#fff;border-radius:.5em}.filepond--file .filepond--file-status{margin-left:auto;margin-right:2.25em}.filepond--file .filepond--processing-complete-indicator{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:3}.filepond--file .filepond--file-action-button,.filepond--file .filepond--processing-complete-indicator,.filepond--file .filepond--progress-indicator{position:absolute}.filepond--file [data-align*=left]{left:.5625em}.filepond--file [data-align*=right]{right:.5625em}.filepond--file [data-align*=center]{left:calc(50% - .8125em)}.filepond--file [data-align*=bottom]{bottom:1.125em}.filepond--file [data-align=center]{top:calc(50% - .8125em)}.filepond--file .filepond--progress-indicator{margin-top:.1875em}.filepond--file .filepond--progress-indicator[data-align*=right]{margin-right:.1875em}.filepond--file .filepond--progress-indicator[data-align*=left]{margin-left:.1875em}[data-filepond-item-state*=error] .filepond--file-info,[data-filepond-item-state*=invalid] .filepond--file-info,[data-filepond-item-state=cancelled] .filepond--file-info{margin-right:2.25em}[data-filepond-item-state~=processing] .filepond--file-status-sub{opacity:0}[data-filepond-item-state~=processing] .filepond--action-abort-item-processing~.filepond--file-status .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-error] .filepond--file-status-sub{opacity:0}[data-filepond-item-state=processing-error] .filepond--action-retry-item-processing~.filepond--file-status .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-complete] .filepond--action-revert-item-processing svg{-webkit-animation:fall .5s linear .125s both;animation:fall .5s linear .125s both}[data-filepond-item-state=processing-complete] .filepond--file-status-sub{opacity:.5}[data-filepond-item-state=processing-complete] .filepond--file-info-sub,[data-filepond-item-state=processing-complete] .filepond--processing-complete-indicator:not([style*=hidden])~.filepond--file-status .filepond--file-status-sub{opacity:0}[data-filepond-item-state=processing-complete] .filepond--action-revert-item-processing~.filepond--file-info .filepond--file-info-sub{opacity:.5}[data-filepond-item-state*=error] .filepond--file-wrapper,[data-filepond-item-state*=error] .filepond--panel,[data-filepond-item-state*=invalid] .filepond--file-wrapper,[data-filepond-item-state*=invalid] .filepond--panel{-webkit-animation:shake .65s linear both;animation:shake .65s linear both}[data-filepond-item-state*=busy] .filepond--progress-indicator svg{-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes shake{10%,90%{-webkit-transform:translateX(-.0625em);transform:translateX(-.0625em)}20%,80%{-webkit-transform:translateX(.125em);transform:translateX(.125em)}30%,50%,70%{-webkit-transform:translateX(-.25em);transform:translateX(-.25em)}40%,60%{-webkit-transform:translateX(.25em);transform:translateX(.25em)}}@keyframes shake{10%,90%{-webkit-transform:translateX(-.0625em);transform:translateX(-.0625em)}20%,80%{-webkit-transform:translateX(.125em);transform:translateX(.125em)}30%,50%,70%{-webkit-transform:translateX(-.25em);transform:translateX(-.25em)}40%,60%{-webkit-transform:translateX(.25em);transform:translateX(.25em)}}@-webkit-keyframes fall{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}70%{opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}to{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes fall{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}70%{opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}to{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.filepond--hopper[data-hopper-state=drag-over]>*{pointer-events:none}.filepond--hopper[data-hopper-state=drag-over]:after{content:"";position:absolute;left:0;top:0;right:0;bottom:0;z-index:100}.filepond--progress-indicator{z-index:103}.filepond--file-action-button{z-index:102}.filepond--file-status{z-index:101}.filepond--file-info{z-index:100}.filepond--item{position:absolute;top:0;left:0;right:0;z-index:1;padding:0;margin:.25em;will-change:transform,opacity}.filepond--item>.filepond--panel{z-index:-1}.filepond--item>.filepond--panel .filepond--panel-bottom{box-shadow:0 .0625em .125em -.0625em rgba(0,0,0,.25)}.filepond--item>.filepond--file-wrapper,.filepond--item>.filepond--panel{transition:opacity .15s ease-out}.filepond--item[data-drag-state]{cursor:-webkit-grab;cursor:grab}.filepond--item[data-drag-state]>.filepond--panel{transition:box-shadow .125s ease-in-out;box-shadow:0 0 0 transparent}.filepond--item[data-drag-state=drag]{cursor:-webkit-grabbing;cursor:grabbing}.filepond--item[data-drag-state=drag]>.filepond--panel{box-shadow:0 .125em .3125em rgba(0,0,0,.325)}.filepond--item[data-drag-state]:not([data-drag-state=idle]){z-index:2}.filepond--item-panel{background-color:#64605e}[data-filepond-item-state=processing-complete] .filepond--item-panel{background-color:#369763}[data-filepond-item-state*=error] .filepond--item-panel,[data-filepond-item-state*=invalid] .filepond--item-panel{background-color:#c44e47}.filepond--item-panel{border-radius:.5em;transition:background-color .25s}.filepond--list-scroller{position:absolute;top:0;left:0;right:0;margin:0;will-change:transform}.filepond--list-scroller[data-state=overflow] .filepond--list{bottom:0;right:0}.filepond--list-scroller[data-state=overflow]{overflow-y:scroll;overflow-x:hidden;-webkit-overflow-scrolling:touch;-webkit-mask:linear-gradient(180deg,#000 calc(100% - .5em),transparent);mask:linear-gradient(180deg,#000 calc(100% - .5em),transparent)}.filepond--list-scroller::-webkit-scrollbar{background:transparent}.filepond--list-scroller::-webkit-scrollbar:vertical{width:1em}.filepond--list-scroller::-webkit-scrollbar:horizontal{height:0}.filepond--list-scroller::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.3);border-radius:99999px;border:.3125em solid transparent;background-clip:content-box}.filepond--list.filepond--list{position:absolute;top:0;margin:0;padding:0;list-style-type:none;will-change:transform}.filepond--list{left:.75em;right:.75em}.filepond--root[data-style-panel-layout~=integrated]{width:100%;height:100%;max-width:none;margin:0}.filepond--root[data-style-panel-layout~=circle] .filepond--panel-root,.filepond--root[data-style-panel-layout~=integrated] .filepond--panel-root{border-radius:0}.filepond--root[data-style-panel-layout~=circle] .filepond--panel-root>*,.filepond--root[data-style-panel-layout~=integrated] .filepond--panel-root>*{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--drop-label,.filepond--root[data-style-panel-layout~=integrated] .filepond--drop-label{bottom:0;height:auto;display:flex;justify-content:center;align-items:center;z-index:7}.filepond--root[data-style-panel-layout~=circle] .filepond--item-panel,.filepond--root[data-style-panel-layout~=integrated] .filepond--item-panel{display:none}.filepond--root[data-style-panel-layout~=compact] .filepond--list-scroller,.filepond--root[data-style-panel-layout~=integrated] .filepond--list-scroller{overflow:hidden;height:100%;margin-top:0;margin-bottom:0}.filepond--root[data-style-panel-layout~=compact] .filepond--list,.filepond--root[data-style-panel-layout~=integrated] .filepond--list{left:0;right:0;height:100%}.filepond--root[data-style-panel-layout~=compact] .filepond--item,.filepond--root[data-style-panel-layout~=integrated] .filepond--item{margin:0}.filepond--root[data-style-panel-layout~=compact] .filepond--file-wrapper,.filepond--root[data-style-panel-layout~=integrated] .filepond--file-wrapper{height:100%}.filepond--root[data-style-panel-layout~=compact] .filepond--drop-label,.filepond--root[data-style-panel-layout~=integrated] .filepond--drop-label{z-index:7}.filepond--root[data-style-panel-layout~=circle]{border-radius:99999rem;overflow:hidden}.filepond--root[data-style-panel-layout~=circle]>.filepond--panel{border-radius:inherit}.filepond--root[data-style-panel-layout~=circle]>.filepond--panel>*{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--file-info,.filepond--root[data-style-panel-layout~=circle] .filepond--file-status{display:none}.filepond--root[data-style-panel-layout~=circle] .filepond--action-edit-item{opacity:1!important;visibility:visible!important}@media not all and (min-resolution:0.001dpcm){@supports (-webkit-appearance:none) and (stroke-color:transparent){.filepond--root[data-style-panel-layout~=circle]{will-change:transform}}}.filepond--panel-root{border-radius:.5em;background-color:#f1f0ef}.filepond--panel{position:absolute;left:0;top:0;right:0;margin:0;height:100%!important;pointer-events:none}.filepond-panel:not([data-scalable=false]){height:auto!important}.filepond--panel[data-scalable=false]>div{display:none}.filepond--panel[data-scalable=true]{-webkit-transform-style:preserve-3d;transform-style:preserve-3d;background-color:transparent!important;border:none!important}.filepond--panel-bottom,.filepond--panel-center,.filepond--panel-top{position:absolute;left:0;top:0;right:0;margin:0;padding:0}.filepond--panel-bottom,.filepond--panel-top{height:.5em}.filepond--panel-top{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important;border-bottom:none!important}.filepond--panel-top:after{content:"";position:absolute;height:2px;left:0;right:0;bottom:-1px;background-color:inherit}.filepond--panel-bottom,.filepond--panel-center{will-change:transform;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:left top;transform-origin:left top;-webkit-transform:translate3d(0,.5em,0);transform:translate3d(0,.5em,0)}.filepond--panel-bottom{border-top-left-radius:0!important;border-top-right-radius:0!important;border-top:none!important}.filepond--panel-bottom:before{content:"";position:absolute;height:2px;left:0;right:0;top:-1px;background-color:inherit}.filepond--panel-center{height:100px!important;border-top:none!important;border-bottom:none!important;border-radius:0!important}.filepond--panel-center:not([style]){visibility:hidden}.filepond--progress-indicator{position:static;width:1.25em;height:1.25em;color:#fff;margin:0;pointer-events:none;will-change:transform,opacity}.filepond--progress-indicator svg{width:100%;height:100%;vertical-align:top;transform-box:fill-box}.filepond--progress-indicator path{fill:none;stroke:currentColor}.filepond--list-scroller{z-index:6}.filepond--drop-label{z-index:5}.filepond--drip{z-index:3}.filepond--root>.filepond--panel{z-index:2}.filepond--browser{z-index:1}.filepond--root{box-sizing:border-box;position:relative;margin-bottom:1em;font-size:1rem;line-height:normal;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;font-weight:450;text-align:left;text-rendering:optimizeLegibility;direction:ltr;contain:layout style size}.filepond--root *{box-sizing:inherit;line-height:inherit}.filepond--root :not(text){font-size:inherit}.filepond--root[data-disabled]{pointer-events:none}.filepond--root[data-disabled] .filepond--list-scroller{pointer-events:all}.filepond--root[data-disabled] .filepond--list{pointer-events:none}.filepond--root .filepond--drop-label{min-height:4.75em}.filepond--root .filepond--list-scroller{margin-top:1em;margin-bottom:1em}.filepond--root .filepond--credits{position:absolute;right:0;opacity:.175;line-height:.85;font-size:11px;color:inherit;text-decoration:none;z-index:3;bottom:-14px}.filepond--root .filepond--credits[style]{top:0;bottom:auto;margin-top:14px}
--------------------------------------------------------------------------------
/main.aardio:
--------------------------------------------------------------------------------
1 | //扫码传文件 - v2.6 (增强版)
2 | import inet.url;
3 | import string.html;
4 | import fonts.fontAwesome;
5 | import win.ui;
6 | import win.clip;
7 | import win.clip.viewer;
8 | import fsys;
9 | import fsys.info;
10 | import fsys.config;
11 | import fsys.dlg.dir;
12 | import fsys.update.simpleMain;
13 | import process;
14 | import web.socket.server;
15 | import qrencode.bitmap;
16 | import win.ui.simpleWindow2;
17 | import win.ui.menu;
18 | /*DSG{{*/
19 | var winform = win.form(text="qrfs - 扫码快传 v2.6";right=807;bottom=465;bgcolor=0xFFFFFF;border="none";max=false)
20 | winform.add(
21 | bk={cls="bk";left=-2;top=-5;right=810;bottom=29;bgcolor=0xF0F0F0;dl=1;dr=1;dt=1;forecolor=0xC0DCC0;linearGradient=0;z=10};
22 | bkplus={cls="bkplus";text="qrfs - 扫码快传 v2.6";left=35;top=3;right=220;bottom=25;align="left";color=0x5A5A5A;dl=1;dt=1;z=11};
23 | btnOpen={cls="plus";text='\uF115';left=441;top=36;right=476;bottom=61;dr=1;dt=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=6};
24 | btnOpenUpload={cls="plus";text="打开上传目录";left=506;top=32;right=647;bottom=61;dl=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8;top=2}};iconText='\uF045';notify=1;textPadding={left=20};z=12};
25 | btnStart={cls="plus";text="重启服务";left=627;top=72;right=760;bottom=118;align="left";bgcolor=0xE3E4DB;dr=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-19;name='FontAwesome');padding={left=16}};iconText='\uF233';notify=1;textPadding={left=45;bottom=2};z=5};
26 | editDocumentRoot={cls="plus";left=131;top=35;right=430;bottom=59;align="left";border={bottom=1;color=0xFF808080};dl=1;dr=1;dt=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=2};z=8};
27 | editHost={cls="plus";left=131;top=64;right=430;bottom=88;align="left";border={bottom=1;color=0xFF808080};dr=1;dt=1;editable="edit";font=LOGFONT(h=-16);textPadding={bottom=2};z=19};
28 | editPassword={cls="plus";left=131;top=95;right=430;bottom=119;align="left";border={bottom=1;color=0xFF808080};dr=1;dt=1;editable="edit";font=LOGFONT(h=-16);password=1;textPadding={right=24;bottom=2};z=14};
29 | editPort={cls="plus";left=501;top=67;right=587;bottom=91;align="left";border={bottom=1;color=0xFF808080};dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=9};
30 | plus={cls="plus";text="密码:";left=71;top=98;right=129;bottom=122;align="right";dl=1;dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=13};
31 | plus2={cls="plus";left=38;top=130;right=472;bottom=424;align="left";border={color=0xFF008000;radius=8;width=1};db=1;dl=1;dr=1;dt=1;font=LOGFONT(h=-14);textPadding={left=16};valign="top";z=1};
32 | plus3={cls="plus";text="自定义网址:";left=9;top=68;right=129;bottom=92;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=20};
33 | qr={cls="plus";left=499;top=132;right=760;bottom=418;db=1;dr=1;dt=1;repeat="scale";z=7};
34 | radioQrClipboard={cls="radiobutton";text="共享剪贴板";left=685;top=435;right=772;bottom=454;bgcolor=0xFFFFFF;db=1;dr=1;z=18};
35 | radioQrRootDir={cls="radiobutton";text="根目录";left=502;top=435;right=589;bottom=454;bgcolor=0xFFFFFF;db=1;dr=1;z=16};
36 | radioQrUploadDir={cls="radiobutton";text="上传目录";left=593;top=435;right=680;bottom=454;bgcolor=0xFFFFFF;db=1;dr=1;z=17};
37 | static={cls="plus";text="端口:";left=439;top=68;right=502;bottom=92;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=3};
38 | static2={cls="plus";text="网站根目录:";left=15;top=38;right=129;bottom=62;align="right";dl=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=4};
39 | syslink={cls="syslink";text='开源项目';left=43;top=437;right=176;bottom=457;bgcolor=0xFFFFFF;db=1;dl=1;z=15};
40 | txtMessage={cls="richedit";left=42;top=132;right=469;bottom=418;autohscroll=false;db=1;dl=1;dr=1;dt=1;link=1;multiline=1;vscroll=1;z=2}
41 | )
42 | /*}}*/
43 |
44 | if( fsys.update.simpleMain(
45 | "qrfs - 扫码快传",
46 | "http://d.aardio.com/qrfs/update/",
47 | io.appData("/aardio/std/qrfs/app/update"),
48 | function(version,description,status){})){
49 | return 0;
50 | }
51 |
52 | config = fsys.config(io.appData("aardio/std/qrfs"));
53 |
54 | import sessionHandler.default;
55 | sessionHandler.default.root = io.appData("aardio/std/qrfs/session")
56 |
57 | if( io.exist(config.winform.txtMessage) ){
58 | winform.txtMessage.text = config.winform.txtMessage;
59 | }
60 | else {
61 | winform.txtMessage.text = io.getSpecial(0x5/*_CSIDL_MYDOCUMENTS*/)
62 | }
63 |
64 | if(config.winform.qrDir=="upload"){
65 | winform.radioQrUploadDir.checked = true;
66 | }
67 | elseif(config.winform.qrDir=="clipboard"){
68 | winform.radioQrClipboard.checked = true;
69 | }
70 | else {
71 | winform.radioQrRootDir.checked = true;
72 | }
73 | winform.editHost.text = config.winform.editHost;
74 |
75 | var wsrv = web.socket.server();
76 | var srvHttp = wsrv.httpServer;
77 | /*
78 | wsrv.httpServer 是实现单线程异步 HTTP 服务端的 wsock.tcp.asynHttpServer 对象。
79 | 浏览器组件发起异步 HTTP 请求支持 wsock.tcp.asynHttpServer。请不要用 inet.http 等
80 | 阻塞请求同一线程创建的 asynHttpServer,这会导致 asynHttpServer 没有机会响应请求而导致死锁,
81 | 如果确有这样的需求,可创建线程发起请求,或改用基于多线程的 wsock.tcp.simpleHttpServer。
82 | */
83 | srvHttp.documentRoot = winform.txtMessage.text;
84 | srvHttp.userToken = string.random(18);
85 | winform.editPassword.text = srvHttp.userToken;
86 |
87 | var cacheSysIcons = {}
88 | var getSysIconIndex = function(path){
89 | var sfi;
90 | sfi = fsys.info.get(path, 0x100/*_SHGFI_ICON*/ | 0x4000/*_SHGFI_SYSICONINDEX*/|0x10/*_SHGFI_USEFILEATTRIBUTES*/,
91 | ..fsys.isDir(path)?0x10/*_FILE_ATTRIBUTE_DIRECTORY*/?0x80/*_FILE_ATTRIBUTE_NORMAL*/);
92 |
93 | if( !(sfi.returnValue ) ) return;
94 |
95 | if(!cacheSysIcons[sfi.iIcon]){
96 | var bmp = ..gdip.bitmap(sfi.hIcon,1/*_IMAGE_ICON*/);
97 | if(bmp){
98 | cacheSysIcons[sfi.iIcon] = bmp.saveToBuffer(".png");
99 | bmp.dispose();
100 | }
101 | }
102 | if(sfi.hIcon)::DestroyIcon(sfi.hIcon);
103 | return sfi.iIcon;
104 | }
105 |
106 | // 文件大小格式化
107 | var formatSize = function(size){
108 | if(size===null) return "";
109 | if(size < 1024) return size + " B";
110 | if(size < 1024*1024) return string.format("%.1f KB", size/1024);
111 | if(size < 1024*1024*1024) return string.format("%.1f MB", size/(1024*1024));
112 | return string.format("%.1f GB", size/(1024*1024*1024));
113 | }
114 |
115 | var cacheClientIps = {}
116 | srvHttp.run(
117 | function(response,request,session){
118 |
119 | response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
120 | response.headers["Pragma"] = "no-cache";
121 | response.headers["Expires"] = "0";
122 |
123 | var token = request.get["t"] : session["token"];
124 | if( #srvHttp.userToken && (token != srvHttp.userToken) ){
125 | winform.txtMessage.printf("客户端:%s 连接被拒绝",request.remoteAddr);
126 | response.errorStatus(401)
127 | return;
128 | }
129 | session["token"] = token;
130 |
131 | if(!cacheClientIps[request.remoteAddr]){
132 | winform.txtMessage.printf("客户端:%s 已连接",request.remoteAddr);
133 | cacheClientIps[request.remoteAddr] = true;
134 | }
135 |
136 | response.headers["Access-Control-Allow-Origin"] = "*";
137 | response.headers["Access-Control-Allow-Headers"] = "*"
138 |
139 | if(request.path=="/main.aardio" && request.get["icon"]){
140 | var iconIdx = tonumber(request.get["icon"]);
141 | if(iconIdx!==null && cacheSysIcons[iconIdx]){
142 | response.contentType = "image/png";
143 | response.write(cacheSysIcons[iconIdx])
144 | return;
145 | }
146 | response.errorStatus(404);
147 | return;
148 | }
149 |
150 | if(request.path=="/upload/main.aardio"){
151 | if(request.method=="DELETE"){
152 | var path = request.postData();
153 | if(path && string.startsWith(path,"/upload/")){
154 | if(string.find(path,"\.\.")) { response.errorStatus(403); return; }
155 |
156 | path = ..io.joinpath(srvHttp.documentRoot,path)
157 | if(io.exist(path)){
158 | io.remove(path);
159 | winform.txtMessage.print("已删除:" + path);
160 | response.close();
161 | return;
162 | }
163 | }
164 | response.errorStatus(404);
165 | return;
166 | }
167 |
168 | fileData = request.postFileData()
169 | if(fileData){
170 | io.createDir(..io.joinpath(srvHttp.documentRoot,"upload"))
171 | winform.txtMessage.print(..io.joinpath(srvHttp.documentRoot,"upload"))
172 |
173 | var fileName = ..io.joinpath(srvHttp.documentRoot,"upload",fileData.filepond.filename)
174 | var ok,err = fileData.filepond.save(fileName);
175 | if(!ok){ response.error(err); }
176 |
177 | winform.txtMessage.text = 'HTTP 服务端已启动: \n';
178 | winform.txtMessage.print( srvHttp.getUrl(,true) + "/?t=" + srvHttp.userToken );
179 | winform.txtMessage.print( "" );
180 | winform.txtMessage.print( "上传成功:" + fileName );
181 |
182 | response.contentType = "text/plain";
183 | response.write("/upload/",fileData.filepond.filename)
184 | return response.close()
185 | }
186 | }
187 |
188 | if(!fsys.isDir(request.path) && request.path!="/" ) {
189 | if( ..io.exist(request.path) && (!_STUDIO_INVOKED || request.path!="/main.aardio") )
190 | return response.loadcode(request.path)
191 | else {
192 | request.path = fsys.getParentDir(request.path)
193 | }
194 | }
195 |
196 | response.write(`
197 |
198 |
199 |
200 |
201 |
202 | qrfs - 扫码快传
203 |
204 |
205 |
206 |
353 |
354 |
355 |
356 |
357 |
358 |
413 | `)
414 |
415 | if(request.path!="//"){
416 | response.write(`
417 |
420 | `)
438 | }
439 |
440 | var pathDisplay = string.html.escape(request.path);
441 | var backButton = "";
442 | var isUploadDir = (request.path=="/upload/");
443 |
444 | if(request.path=="//"){
445 | pathDisplay = "共享剪贴板";
446 | backButton = `返回首页`;
447 | }
448 | elseif(request.path=="/upload/"){
449 | pathDisplay = "上传目录";
450 | backButton = `返回首页`;
451 | }
452 | elseif(request.path=="/") {
453 | pathDisplay = "文件列表";
454 | backButton = "";
455 | }
456 | else {
457 | pathDisplay = string.html.escape(inet.url.decode(request.path));
458 | var parentPath = fsys.getParentDir(request.path);
459 | if(!#parentPath) parentPath = "/";
460 | backButton = `返回上一级`;
461 | }
462 |
463 | response.write(`` + pathDisplay + `` + backButton + `
`);
464 |
465 | if(request.path=="//"){
466 | var clipText = win.clip.read() : "";
467 | response.write(`
468 |
469 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
514 | `)
515 | return;
516 | }
517 |
518 | response.write(``);
519 |
520 | if(request.path=="/" && ..io.exist("/upload/")){
521 | response.write('
上传目录 (可删除) \r\n');
523 | }
524 |
525 | if(request.path=="/") {
526 | response.write('
共享剪贴板 \r\n');
528 | }
529 |
530 | var file,dir = fsys.list(request.path,,"*.*");
531 | var hasContent = false;
532 |
533 | for(i=1;#dir;1){
534 | if(dir[i]==="upload" && request.path=="/") continue;
535 | hasContent = true;
536 | var iconIdx = getSysIconIndex(dir[dir[i]]);
537 | response.write('
',string.html.escape(dir[i]),' \r\n');
540 | }
541 |
542 | for(i=1;#file;1){
543 | hasContent = true;
544 | var iconIdx = getSysIconIndex(file[file[i]]);
545 | var encodedPath = inet.url.append(request.path, inet.url.encode(file[i]));
546 | var filePath = string.replace(request.path + file[i], "//", "/");
547 | // 获取文件大小
548 | var fileSize = io.getSize( io.joinpath(srvHttp.documentRoot, request.path, file[i]) );
549 |
550 | response.write('
',string.html.escape(file[i]),'');
552 |
553 | // 显示文件大小
554 | response.write('' + formatSize(fileSize) + '');
555 |
556 | if(isUploadDir){
557 | var jsPath = string.replace(filePath,"'","\\'");
558 | response.write('');
560 | }
561 | response.write(' \r\n');
562 | }
563 |
564 | if(!hasContent && request.path!="/"){
565 | response.write('- 📭 目录为空
');
566 | }
567 |
568 | response.write("