Explorar o código

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 %!s(int64=13) %!d(string=hai) anos
pai
achega
fd0ccba

+ 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