浏览代码

HUE-981 [fb] Fetch next block should be dynamic

Fetching a next, previous, first, or last block when viewing a file
should be dynamic. This addresses the memory leak in spawning.
No connections are cut off early, unless the user chooses to go to another page.
abec 13 年之前
父节点
当前提交
fd0ccba
共有 2 个文件被更改,包括 242 次插入177 次删除
  1. 239 175
      apps/filebrowser/src/filebrowser/templates/display.mako
  2. 3 2
      apps/filebrowser/src/filebrowser/views.py

+ 239 - 175
apps/filebrowser/src/filebrowser/templates/display.mako

@@ -33,185 +33,249 @@ ${ commonheader(_('%(filename)s - File Viewer') % dict(filename=truncate(filenam
 
 
 <div class="container-fluid">
-	% if breadcrumbs:
+  % if breadcrumbs:
         ${fb_components.breadcrumbs(path, breadcrumbs)}
-	%endif
+  %endif
 </div>
 
 <div class="container-fluid">
-	<div class="row-fluid">
-		<div class="span2">
-			<div class="well sidebar-nav">
-				<ul class="nav nav-list">
-					<li class="nav-header">${_('Actions')}</li>
-					% if view['mode'] == "binary":
-				        <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=text&compression=${view['compression']}">${_('View As Text')}</a></li>
-				      % endif
-					  % if view['mode'] == "text":
-				        <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=binary&compression=${view['compression']}">${_('View As Binary')}</a></li>
-				      % endif
-
-				      % if view['compression'] != "gzip" and path.endswith('.gz'):
-				        <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=gzip">${_('Preview As Gzip')}</a></li>
-				      % endif
-
-				      % if view['compression'] != "avro" and view['compression'] != "snappy_avro" and path.endswith('.avro'):
-				        <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=avro">${_('Preview As Avro')}</a></li>
-				      % endif
-
-				      % if view['compression'] and view['compression'] != "none":
-				        <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=none">${_('Stop preview')}</a></li>
-				      % endif
-
-				      % if editable and view['compression'] == "none":
-				        <li><a href="${url('filebrowser.views.edit', path=path_enc)}">${_('Edit File')}</a></li>
-				      % endif
-				       <li><a href="${url('filebrowser.views.download', path=path_enc)}">${_('Download')}</a></li>
-				       <li><a href="${url('filebrowser.views.view', path=dirname_enc)}">${_('View File Location')}</a></li>
-				       <li><a id="refreshBtn">${_('Refresh')}</a></li>
-					<li class="nav-header">${_('Info')}</li>
-					<li>
-						<dl>
-							<dt>${_('Last Modified')}</dt>
-				        	<dd>${date(datetime.datetime.fromtimestamp(stats['mtime']))} ${time(datetime.datetime.fromtimestamp(stats['mtime']))}</dd>
-				        	<dt>${_('User')}</dt>
-				        	<dd>${stats['user']}</dd>
-				        	<dt>${_('Group')}</dt>
-				        	<dd>${stats['group']}</dd>
-				        	<dt>${_('Size')}</dt>
-				        	<dd>${stats['size']|filesizeformat}</dd>
-				        	<dt>${_('Mode')}</dt>
-				        	<dd>${stringformat(stats['mode'], "o")}</dd>
-						</dl>
-					</li>
-				</ul>
-
-			</div>
-		</div>
-		<div class="span10">
-			% if not view['compression'] or view['compression'] in ("none", "avro"):
-			      <div class="pagination">
-			        <%
-			          base_url = url('filebrowser.views.view', path=path_enc)
-			          if view['offset'] == 0:
-			              first_class = "prev disabled"
-			              prev_class = "disabled"
-			              first_href = ""
-			              prev_href = ""
-			          else:
-			              first_class = "prev"
-			              prev_class = ""
-			              first_href = "href=%s?offset=0&length=%d&compression=none title=1 - %d" %(base_url, view['length'], min(view['length'], stats['size']))
-			              prev_href =  "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, max(0, view['offset']-view['length']), view['length'], max(0, view['offset']-view['length']) + 1, min(max(0, view['offset'] - view['length']) + view['length'], stats['size']))
-			          if view['offset'] + view['length'] >= stats['size']:
-			              next_class = "disabled"
-			              last_class = "next disabled"
-			              next_href = ""
-			              last_href = ""
-			          else:
-			              next_class = ""
-			              last_class = "next"
-			              next_href = "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, view['offset'] + view['length'], view['length'], view['offset'] + view['length'] + 1, view['offset'] + (2 * view['length']))
-			              last_href =  "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, stats['size']-(stats['size'] % view['length']), view['length'], stats['size']-(stats['size'] % view['length']) + 1, stats['size'])
-			        %>
-			        ###DEFINE REL
-			        <ul>
-			            <li class="${first_class}"><a ${first_href}>${_('First Block')}</a></li>
-			            <li class="${prev_class}"><a ${prev_href}>${_('Previous Block')}</a></li>
-			            <li class="${next_class}"><a ${next_href}>${_('Next Block')}</a></li>
-			            <li class="${last_class}"><a ${last_href}>${_('Last Block')}</a></li>
-			        </ul>
-
-					<form action="${url('filebrowser.views.view', path=path_enc)}" method="GET" class="form-inline pull-right">
-						<span>${_('Viewing Bytes:')}</span>
-						<input type="text" name="begin" value="${view['offset'] + 1}" class="input-mini" />
-						-
-						<input type="text" value="${view['end']}" name="end" class="input-mini" /> of
-						<span>${stats['size']}</span>
-						<span>${_('(%(length)s B block size)' % dict(length=view['length']))}</span>
-						% if view['mode']:
-							<input type="hidden" name="mode" value="${view['mode']}"/>
-						% endif
-			        </form>
-
-			      </div>
-
-			    % endif
-			%if 'contents' in view:
-		      % if view['masked_binary_data']:
-		      <div class="alert-message warning">${_("Warning: some binary data has been masked out with '&#xfffd'.")}</div>
-		      % endif
-		    % endif
-		      <div>
-		      % if 'contents' in view:
-		             <div><pre>${view['contents']}</pre></div>
-		      % else:
-		        <table>
-		          % for offset, words, masked in view['xxd']:
-		            <tr>
-		              <td><tt>${stringformat(offset, "07x")}:&nbsp;</tt></td>
-		            <td>
-		              <tt>
-		                % for word in words:
-		                  % for byte in word:
-		                    ${stringformat(byte, "02x")}
-		                  % endfor
-		                % endfor
-		              </tt>
-		            </td>
-		            <td>
-		              <tt>
-		                &nbsp;&nbsp;${masked}
-		              </tt>
-		            </td>
-		            </tr>
-		          % endfor
-		        </table>
-		      % endif
-		      </div>
-			  % if not view['compression'] or view['compression'] in ("none", "avro"):
-			      <div class="pagination">
-			        <%
-			          base_url = url('filebrowser.views.view', path=path_enc)
-			          if view['offset'] == 0:
-			              first_class = "prev disabled"
-			              prev_class = "disabled"
-			              first_href = ""
-			              prev_href = ""
-			          else:
-			              first_class = "prev"
-			              prev_class = ""
-			              first_href = "href=%s?offset=0&length=%d&compression=none title=1 - %d" %(base_url, view['length'], min(view['length'], stats['size']))
-			              prev_href =  "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, max(0, view['offset']-view['length']), view['length'], max(0, view['offset']-view['length']) + 1, min(max(0, view['offset'] - view['length']) + view['length'], stats['size']))
-			          if view['offset'] + view['length'] >= stats['size']:
-			              next_class = "disabled"
-			              last_class = "next disabled"
-			              next_href = ""
-			              last_href = ""
-			          else:
-			              next_class = ""
-			              last_class = "next"
-			              next_href = "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, view['offset'] + view['length'], view['length'], view['offset'] + view['length'] + 1, view['offset'] + (2 * view['length']))
-			              last_href =  "href=%s?offset=%d&length=%d&compression=none title=%d - %d" %(base_url, stats['size']-(stats['size'] % view['length']), view['length'], stats['size']-(stats['size'] % view['length']) + 1, stats['size'])
-			        %>
-			        ###DEFINE REL
-			        <ul>
-			            <li class="${first_class}"><a ${first_href}>${_('First Block')}</a></li>
-			            <li class="${prev_class}"><a ${prev_href}>${_('Previous Block')}</a></li>
-			            <li class="${next_class}"><a ${next_href}>${_('Next Block')}</a></li>
-			            <li class="${last_class}"><a ${last_href}>${_('Last Block')}</a></li>
-			        </ul>
-			      </div>
-			    % endif
-		</div>
-	</div>
+  <div class="row-fluid">
+    <div class="span2">
+      <div class="well sidebar-nav">
+        <ul class="nav nav-list">
+          <li class="nav-header">${_('Actions')}</li>
+          % if view['mode'] == "binary":
+            <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=text&compression=${view['compression']}">${_('View As Text')}</a></li>
+          % endif
+
+          % if view['mode'] == "text":
+            <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=binary&compression=${view['compression']}">${_('View As Binary')}</a></li>
+          % endif
+
+          % if view['compression'] != "gzip" and path.endswith('.gz'):
+            <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=gzip">${_('Preview As Gzip')}</a></li>
+          % endif
+
+          % if view['compression'] != "avro" and view['compression'] != "snappy_avro" and path.endswith('.avro'):
+            <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=avro">${_('Preview As Avro')}</a></li>
+          % endif
+
+          % if view['compression'] and view['compression'] != "none":
+            <li><a href="${base_url}?offset=0&length=2000&mode=${view['mode']}&compression=none">${_('Stop preview')}</a></li>
+          % endif
+
+          % if editable and view['compression'] == "none":
+            <li><a href="${url('filebrowser.views.edit', path=path_enc)}">${_('Edit File')}</a></li>
+          % endif
+
+           <li><a href="${url('filebrowser.views.download', path=path_enc)}">${_('Download')}</a></li>
+           <li><a href="${url('filebrowser.views.view', path=dirname_enc)}">${_('View File Location')}</a></li>
+           <li><a id="refreshBtn">${_('Refresh')}</a></li>
+          <li class="nav-header">${_('Info')}</li>
+          <li>
+            <dl>
+              <dt>${_('Last Modified')}</dt>
+              <dd>${date(datetime.datetime.fromtimestamp(stats['mtime']))} ${time(datetime.datetime.fromtimestamp(stats['mtime']))}</dd>
+              <dt>${_('User')}</dt>
+              <dd>${stats['user']}</dd>
+              <dt>${_('Group')}</dt>
+              <dd>${stats['group']}</dd>
+              <dt>${_('Size')}</dt>
+              <dd>${stats['size']|filesizeformat}</dd>
+              <dt>${_('Mode')}</dt>
+              <dd>${stringformat(stats['mode'], "o")}</dd>
+            </dl>
+          </li>
+        </ul>
+
+      </div>
+    </div>
+    <div class="span10">
+      % if not view['compression'] or view['compression'] in ("none", "avro"):
+        <div class="pagination">
+          <ul>
+              <li class="first-block prev disabled"><a href="javascript:void(0);" data-bind="click: firstBlock">${_('First Block')}</a></li>
+              <li class="previous-block disabled"><a href="javascript:void(0);" data-bind="click: previousBlock">${_('Previous Block')}</a></li>
+              <li class="next-block"><a href="javascript:void(0);" data-bind="click: nextBlock">${_('Next Block')}</a></li>
+              <li class="last-block next"><a href="javascript:void(0);" data-bind="click: lastBlock">${_('Last Block')}</a></li>
+          </ul>
+
+          <form action="${url('filebrowser.views.view', path=path_enc)}" method="GET" class="form-inline pull-right">
+            <span>${_('Viewing Bytes:')}</span>
+            <input type="text" name="begin" value="${view['offset'] + 1}" data-bind="value: begin" class="input-mini" />
+            -
+            <input type="text" name="end" value="${view['end']}" data-bind="value: end" class="input-mini" /> of
+            <span>${stats['size']}</span>
+            <span>${_('(%(length)s B block size)' % dict(length=view['length']))}</span>
+            % if view['mode']:
+              <input type="hidden" name="mode" value="${view['mode']}"/>
+            % endif
+          </form>
+
+        </div>
+      % endif
+
+      %if 'contents' in view:
+        % if view['masked_binary_data']:
+        <div class="alert-message warning">${_("Warning: some binary data has been masked out with '&#xfffd'.")}</div>
+        % endif
+      % endif
+
+      <div>
+      % if 'contents' in view:
+        <div id="file-contents"><pre>${view['contents']}</pre></div>
+      % else:
+        <table>
+          % for offset, words, masked in view['xxd']:
+          <tr>
+            <td><tt>${stringformat(offset, "07x")}:&nbsp;</tt></td>
+            <td>
+              <tt>
+                % for word in words:
+                  % for byte in word:
+                    ${stringformat(byte, "02x")}
+                  % endfor
+                % endfor
+              </tt>
+            </td>
+            <td>
+              <tt>
+                &nbsp;&nbsp;${masked}
+              </tt>
+            </td>
+          </tr>
+          % endfor
+        </table>
+      % endif
+      </div>
+
+      % if not view['compression'] or view['compression'] in ("none", "avro"):
+        <div class="pagination">
+          <ul>
+              <li class="first-block prev disabled"><a href="javascript:void(0);" data-bind="click: firstBlock">${_('First Block')}</a></li>
+              <li class="previous-block disabled"><a href="javascript:void(0);" data-bind="click: previousBlock">${_('Previous Block')}</a></li>
+              <li class="next-block"><a href="javascript:void(0);" data-bind="click: nextBlock">${_('Next Block')}</a></li>
+              <li class="last-block next"><a href="javascript:void(0);" data-bind="click: lastBlock">${_('Last Block')}</a></li>
+          </ul>
+        </div>
+      % endif
+    </div>
+  </div>
 </div>
 
-	<script type="text/javascript" charset="utf-8">
-		$(document).ready(function(){
-			$("#refreshBtn").click(function(){
-				window.location.reload();
-			});
-		});
-	</script>
+<script src="/static/ext/js/knockout-2.1.0.js" type="text/javascript" charset="utf-8"></script>
+
+  <script type="text/javascript" charset="utf-8">
+    function displayViewModel(base_url, compression, mode, begin, end, length, size, max_size) {
+      var self = this;
+
+      self.base_url = ko.observable(base_url);
+      self.compression = ko.observable(compression);
+      self.mode = ko.observable(mode);
+      self.begin = ko.observable(begin);
+      self.end = ko.observable(end);
+      self.length = ko.observable(length);
+      self.size = ko.observable(size);
+
+      self.offset = ko.computed(function() {
+        return self.begin() - 1;
+      });
+
+      self.url = ko.computed(function() {
+        return self.base_url()
+          + "?offset=" + self.offset()
+          + "&length=" + self.length()
+          + "&compression="+ self.compression()
+          + "&mode=" + self.mode();
+      });
+
+      var change_length = function() {
+        var length = self.end() - self.offset();
+        if (length > max_size) {
+          length = max_size;
+        }
+        self.length(length);
+      };
+      self.begin.subscribe(change_length);
+      self.end.subscribe(change_length);
+
+      var reloading = false;
+      self.reload = function() {
+        if (!reloading) {
+          reloading = true;
+          window.location.href = self.url();
+        }
+      };
+
+      self.toggleDisables = function() {
+        if (self.offset() + self.length() >= self.size()) {
+          $(".next-block").addClass("disabled");
+          $(".last-block").addClass("disabled");
+        } else {
+          $(".next-block").removeClass("disabled");
+          $(".last-block").removeClass("disabled");
+        }
+
+        if (self.offset() <= 0) {
+          $(".first-block").addClass("disabled");
+          $(".previous-block").addClass("disabled");
+        } else {
+          $(".first-block").removeClass("disabled");
+          $(".previous-block").removeClass("disabled");
+        }
+      };
+
+      self.begin.subscribe(self.toggleDisables);
+      self.end.subscribe(self.toggleDisables);
+
+      var changeBlock = function(begin, end) {
+        if (begin < self.size() && end > 0 && end > begin && !reloading) {
+          self.begin(begin);
+          self.end(end);
+          self.toggleDisables();
+          self.reload();
+        }
+      };
+
+      self.nextBlock = function() {
+        var offset = self.offset() + self.length();
+        changeBlock(offset + 1, offset + self.length());
+      };
+
+      self.previousBlock = function() {
+        var offset = ( self.offset() >= self.length() ) ? self.offset() - self.length() : 0;
+        changeBlock(offset + 1, offset + self.length());
+      };
+
+      self.lastBlock = function() {
+        var offset = self.size() - self.length();
+        if (offset < 0) {
+          offset = 0;
+        }
+        changeBlock(offset + 1, offset + self.length());
+      };
+
+      self.firstBlock = function() {
+        changeBlock(1, self.length());
+      }
+    };
+    var viewModel = new displayViewModel(
+      "${ base_url }",
+      "${view['compression']}",
+      "${ view['mode'] }",
+      ${view['offset'] + 1},
+      ${view['end']},
+      ${view['length']},
+      ${stats['size']},
+      ${view['max_chunk_size']}
+    );
+
+    $(window).load(function(){
+      $("#refreshBtn").click(function(){
+        window.location.reload();
+      });
+      ko.applyBindings(viewModel);
+      viewModel.toggleDisables();
+    });
+  </script>
 ${ commonfooter(messages) | n,unicode }

+ 3 - 2
apps/filebrowser/src/filebrowser/views.py

@@ -50,7 +50,7 @@ from avro import datafile, io
 
 from desktop.lib import i18n, paginator
 from desktop.lib.conf import coerce_bool
-from desktop.lib.django_util import make_absolute, render, render_json, format_preserving_redirect
+from desktop.lib.django_util import make_absolute, render, render_json, format_preserving_redirect, render_to_string
 from desktop.lib.exceptions_renderable import PopupException
 from filebrowser.conf import MAX_SNAPPY_DECOMPRESSION_SIZE
 from filebrowser.lib.archives import archive_factory
@@ -606,7 +606,8 @@ def display(request, path):
         'dirname': dirname,
         'mode': mode,
         'compression': compression,
-        'size': stats['size']
+        'size': stats['size'],
+        'max_chunk_size': str(MAX_CHUNK_SIZE_BYTES)
     }
     data["filename"] = os.path.basename(path)
     data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE