filer.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SeaweedFS Filer {{ .Version }}</title>
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <link rel="stylesheet" href="/seaweedfsstatic/bootstrap/3.3.1/css/bootstrap.min.css">
  7. <style>
  8. body {
  9. padding-bottom: 128px;
  10. }
  11. #drop-area {
  12. border: 1px transparent;
  13. margin-top: 5px;
  14. }
  15. #drop-area.highlight {
  16. border-color: purple;
  17. border: 2px dashed #ccc;
  18. }
  19. .button {
  20. display: inline-block;
  21. padding: 2px;
  22. background: #ccc;
  23. cursor: pointer;
  24. border-radius: 2px;
  25. border: 1px solid #ccc;
  26. float: right;
  27. margin-left: 2px;
  28. margin-bottom: 0;
  29. }
  30. label {
  31. font-weight: normal;
  32. }
  33. .button:hover {
  34. background: #ddd;
  35. }
  36. #fileElem {
  37. display: none;
  38. }
  39. td, th {
  40. vertical-align: bottom;
  41. }
  42. .table-hover > tbody > tr:hover > * > div.operations {
  43. display: block;
  44. }
  45. .table > tbody > tr {
  46. height: 39px;
  47. }
  48. div.operations {
  49. display: none;
  50. }
  51. .footer {
  52. position: absolute;
  53. bottom: 0px;
  54. right: 5%;
  55. min-width: 25%;
  56. border-left: 1px solid #ccc;
  57. border-right: 1px solid #ccc;
  58. }
  59. .add-files {
  60. font-size: 46px;
  61. text-align: center;
  62. border: 1px dashed #999;
  63. padding-bottom: 9px;
  64. margin: 0 2px;
  65. }
  66. </style>
  67. </head>
  68. <body>
  69. <div class="container">
  70. <div class="page-header">
  71. <h1>
  72. <a href="https://github.com/seaweedfs/seaweedfs"><img src="/seaweedfsstatic/seaweed50x50.png"></img></a>
  73. SeaweedFS Filer <small>{{ .Version }}</small>
  74. </h1>
  75. </div>
  76. <div class="row">
  77. <div>
  78. <div class="btn-group btn-group-sm pull-right" role="group" style="margin-top:3px;">
  79. <label class="btn btn-default" onclick="handleCreateDir()">
  80. <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> New Folder
  81. </label>
  82. <label class="btn btn-default" for="fileElem">
  83. <span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> Upload
  84. </label>
  85. </div>
  86. <ol class="breadcrumb">
  87. {{ range $entry := .Breadcrumbs }}
  88. <li><a href="{{ printpath $entry.Link }}">
  89. {{ $entry.Name }}
  90. </a></li>
  91. {{ end }}
  92. </ol>
  93. </div>
  94. </div>
  95. <div class="row" id="drop-area">
  96. <form class="upload-form">
  97. <input type="file" id="fileElem" multiple onchange="handleFiles(this.files)">
  98. {{ if .EmptyFolder }}
  99. <div class="row add-files">
  100. +
  101. </div>
  102. {{ else }}
  103. <table width="100%" class="table table-hover">
  104. {{ $path := .Path }}
  105. {{ $showDirDel := .ShowDirectoryDelete }}
  106. {{ range $entry_index, $entry := .Entries }}
  107. <tr>
  108. <td>
  109. {{ if $entry.IsDirectory }}
  110. <span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span>&nbsp;
  111. <a href="{{ printpath $path "/" $entry.Name "/"}}" >
  112. {{ $entry.Name }}
  113. </a>
  114. {{ else }}
  115. <a href="{{ printpath $path "/" $entry.Name }}" >
  116. {{ $entry.Name }}
  117. </a>
  118. {{ end }}
  119. </td>
  120. <td align="right" nowrap>
  121. {{ if not $entry.IsDirectory }}
  122. {{ $entry.Mime }}&nbsp;
  123. {{ end }}
  124. </td>
  125. <td align="right" nowrap>
  126. {{ if not $entry.IsDirectory }}
  127. {{ $entry.Size | humanizeBytes }}&nbsp;
  128. {{ end }}
  129. </td>
  130. <td align="right" nowrap>
  131. {{ $entry.Timestamp.Format "2006-01-02 15:04" }}
  132. </td>
  133. <td style="width:75px">
  134. <div class="btn-group btn-group-xs pull-right operations" role="group">
  135. <label class="btn" onclick="handleRename('{{ $entry.Name }}', '{{ printpath $path "/" }}')">
  136. <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
  137. </label>
  138. {{ if and $entry.IsDirectory $showDirDel }}
  139. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name "/" }}')">
  140. <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
  141. </label>
  142. {{ end }}
  143. {{ if not $entry.IsDirectory }}
  144. <label class="btn" onclick="handleDelete('{{ printpath $path "/" $entry.Name }}')">
  145. <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
  146. </label>
  147. {{ end }}
  148. </div>
  149. </td>
  150. </tr>
  151. {{ end }}
  152. </table>
  153. {{ end }}
  154. </form>
  155. </div>
  156. {{ if .ShouldDisplayLoadMore }}
  157. <div class="row">
  158. <a href={{ print .Path "?limit=" .Limit "&lastFileName=" .LastFileName }} >
  159. Load more
  160. </a>
  161. </div>
  162. {{ end }}
  163. <br/>
  164. <br/>
  165. <div id="progress-area" class="footer" style="display: none;">
  166. </div>
  167. </div>
  168. </body>
  169. <script type="text/javascript">
  170. // ************************ Drag and drop ***************** //
  171. let dropArea = document.getElementById("drop-area");
  172. let progressArea = document.getElementById("progress-area");
  173. // Prevent default drag behaviors
  174. ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  175. dropArea.addEventListener(eventName, preventDefaults, false);
  176. document.body.addEventListener(eventName, preventDefaults, false);
  177. });
  178. // Highlight drop area when item is dragged over it
  179. ;['dragenter', 'dragover'].forEach(eventName => {
  180. dropArea.addEventListener(eventName, highlight, false);
  181. });
  182. ;['dragleave', 'drop'].forEach(eventName => {
  183. dropArea.addEventListener(eventName, unhighlight, false);
  184. });
  185. // Handle dropped files
  186. dropArea.addEventListener('drop', handleDrop, false);
  187. function preventDefaults(e) {
  188. e.preventDefault();
  189. e.stopPropagation();
  190. }
  191. function highlight(e) {
  192. dropArea.classList.add('highlight');
  193. }
  194. function unhighlight(e) {
  195. dropArea.classList.remove('highlight');
  196. }
  197. function handleDrop(e) {
  198. var dt = e.dataTransfer;
  199. var files = dt.files;
  200. handleFiles(files);
  201. }
  202. function reloadPage() {
  203. window.location.reload(true);
  204. }
  205. var uploadList = {};
  206. function handleFiles(files) {
  207. files = [...files];
  208. files.forEach(startUpload);
  209. renderProgress();
  210. files.forEach(uploadFile);
  211. }
  212. function startUpload(file, i) {
  213. uploadList[file.name] = {'name': file.name, 'percent': 0, 'finish': false};
  214. }
  215. function renderProgress() {
  216. var values = Object.values(uploadList);
  217. var html = '<table class="table">\n<tr><th>Uploading</th><\/tr>\n';
  218. for (let i of values) {
  219. var progressBarClass = 'progress-bar-striped active';
  220. if (i.percent >= 100) {
  221. progressBarClass = 'progress-bar-success';
  222. }
  223. html += '<tr>\n<td>\n';
  224. html += '<div class="progress" style="margin-bottom: 2px;">\n';
  225. html += '<div class="progress-bar ' + progressBarClass + '" role="progressbar" aria-valuenow="' + '100" aria-valuemin="0" aria-valuemax="100" style="width:' + i.percent + '%;">';
  226. html += '<span style="margin-right: 10px;">' + i.name + '</span>' + i.percent + '%<\/div>';
  227. html += '<\/div>\n<\/td>\n<\/tr>\n';
  228. }
  229. html += '<\/table>\n';
  230. progressArea.innerHTML = html;
  231. if (values.length > 0) {
  232. progressArea.attributes.style.value = '';
  233. }
  234. }
  235. function reportProgress(file, percent) {
  236. var item = uploadList[file]
  237. item.percent = percent;
  238. renderProgress();
  239. }
  240. function finishUpload(file) {
  241. uploadList[file]['finish'] = true;
  242. renderProgress();
  243. var allFinish = true;
  244. for (let i of Object.values(uploadList)) {
  245. if (!i.finish) {
  246. allFinish = false;
  247. break;
  248. }
  249. }
  250. if (allFinish) {
  251. console.log('All Finish');
  252. reloadPage();
  253. }
  254. }
  255. function uploadFile(file, i) {
  256. var url = window.location.href;
  257. var xhr = new XMLHttpRequest();
  258. var fileName = file.name;
  259. xhr.onreadystatechange = function() {
  260. if (xhr.readyState == XMLHttpRequest.DONE) {
  261. finishUpload(fileName)
  262. }
  263. }
  264. xhr.upload.addEventListener('progress', function(e) {
  265. if (e.lengthComputable) {
  266. var percent = Math.ceil((e.loaded / e.total) * 100);
  267. reportProgress(fileName, percent)
  268. }
  269. });
  270. xhr.upload.addEventListener('loadend', function(e) {
  271. finishUpload(fileName);
  272. });
  273. var formData = new FormData();
  274. xhr.open('POST', url, true);
  275. formData.append('file', file);
  276. xhr.send(formData);
  277. }
  278. function handleCreateDir() {
  279. var dirName = prompt('Folder Name:', '');
  280. dirName = dirName.trim();
  281. if (dirName == null || dirName == '') {
  282. return;
  283. }
  284. var baseUrl = window.location.href;
  285. if (!baseUrl.endsWith('/')) {
  286. baseUrl += '/';
  287. }
  288. var url = baseUrl + encodeURIComponent(dirName);
  289. if (!url.endsWith('/')) {
  290. url += '/';
  291. }
  292. var xhr = new XMLHttpRequest();
  293. xhr.open('POST', url, false);
  294. xhr.setRequestHeader('Content-Type', '');
  295. xhr.send();
  296. reloadPage();
  297. }
  298. function handleRename(originName, basePath) {
  299. var newName = prompt('New Name:', originName);
  300. if (newName == null || newName == '') {
  301. return;
  302. }
  303. var url = basePath + encodeURIComponent(newName);
  304. var originPath = basePath + originName;
  305. url += '?mv.from=' + encodeURIComponent(originPath);
  306. var xhr = new XMLHttpRequest();
  307. xhr.open('POST', url, false);
  308. xhr.setRequestHeader('Content-Type', '');
  309. xhr.send();
  310. reloadPage();
  311. }
  312. function handleDelete(path) {
  313. if (!confirm('Are you sure to delete ' + path + '?')) {
  314. return;
  315. }
  316. var url = path;
  317. if (url.endsWith('/')) {
  318. url += '?recursive=true';
  319. }
  320. var xhr = new XMLHttpRequest();
  321. xhr.open('DELETE', url, false);
  322. xhr.send();
  323. reloadPage();
  324. }
  325. </script>
  326. </html>