HDFS-13470. RBF: Add Browse the Filesystem button to the UI.

This commit is contained in:
Inigo Goiri 2020-03-26 08:23:18 -07:00
parent 50f7f6dfd1
commit 679631b188
3 changed files with 879 additions and 1 deletions

View File

@ -0,0 +1,323 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/bootstrap-3.4.1/css/bootstrap-editable.css"/>
<link rel="stylesheet" type="text/css" href="/static/dataTables.bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/hadoop.css" />
<title>Browsing HDFS</title>
</head>
<body>
<header class="navbar navbar-inverse bs-docs-nav" role="banner">
<div class="container">
<div class="navbar-header">
<div class="navbar-brand">Hadoop</div>
</div>
<ul class="nav navbar-nav" id="ui-tabs">
<li><a href="federationhealth.html#tab-overview">Overview</a></li>
<li><a href="federationhealth.html#tab-namenode">Subclusters</a></li>
<li><a href="federationhealth.html#tab-router">Routers</a></li>
<li><a href="federationhealth.html#tab-datanode">Datanodes</a></li>
<li><a href="federationhealth.html#tab-mounttable">Mount table</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Utilities <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#">Browse the file system</a></li>
<li><a href="logs">Logs</a></li>
<li><a href="logLevel">Log Level</a></li>
<li><a href="jmx">Metrics</a></li>
<li><a href="conf">Configuration</a></li>
<li><a href="stacks">Process Thread Dump</a></li>
</ul>
</li>
</ul>
</div>
</header>
<div class="modal" id="file-info" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="file-info-title">File information</h4>
</div>
<div class="modal-body" id="file-info-body">
<div class=row>
<span class="col-xs-4">
<a id="file-info-download">Download</a>
</span>
<span class="col-xs-4">
<a id="file-info-preview-head" style="cursor:pointer">Head the file (first 32K)</a>
</span>
<span class="col-xs-4">
<a id="file-info-preview-tail" style="cursor:pointer">Tail the file (last 32K)</a>
</span>
</div>
<hr />
<div class="panel panel-success" id="file-info-blockinfo-panel">
<div class="panel-heading">
Block information --
<select class="btn btn-default" id="file-info-blockinfo-list">
</select>
</div>
<div class="panel-body" id="file-info-blockinfo-body"></div>
</div>
<div class="panel panel-info" id="file-info-tail" style="display:none">
<div class="panel-heading">File contents</div>
<div class="panel-body">
<div class="input-group-sm">
<textarea class="form-control" style="height: 150px" id="file-info-preview-body"></textarea>
</div>
</div>
</div>
</div>
<div class="modal-footer"><button type="button" class="btn btn-success"
data-dismiss="modal">Close</button></div>
</div>
</div>
</div>
<div class="container">
<div class="page-header">
<h1>Browse Directory</h1>
</div>
<div class="alert alert-danger" id="alert-panel" style="display:none">
<button type="button" class="close" onclick="$('#alert-panel').hide();">&times;</button>
<div class="alert-body" id="alert-panel-body"></div>
</div>
<div class="modal" id="btn-create-directory" tabindex="-1" role="dialog"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close"
data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Create Directory</h4>
</div>
<div class="modal-body">
<div class="form-group">
<div class="input-group">
<span class="input-group-addon" id="new_directory_pwd"></span>
<input type="text" class="form-control" id="new_directory"
placeholder="New Directory Name" />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success"
id="btn-create-directory-send" data-complete-text="Creating...">
Create
</button>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-upload-file" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"><button type="button" class="close"
data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="file-upload-title">Upload File</h4>
</div>
<div class="modal-body" id="file-upload-body">
<input id="modal-upload-file-input" type="file" class="file" multiple>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-success" id="modal-upload-file-button" data-complete-text="Uploading...">Upload</button>
</div>
</div>
</div>
</div>
<div class="modal" id="delete-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h4 class="modal-title" id="delete-modal-title">Delete</h4>
</div>
<div class="modal-body">
<div class="panel-body">
<div id="delete-prompt"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-success" id="delete-button"
data-complete-text="Deleting...">Delete</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-md-10">
<form onsubmit="return false;">
<div class="input-group">
<input type="text" class="form-control" id="directory"/>
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="btn-nav-directory">Go!</button>
</span>
</div>
</form>
</div>
<div class="col-xs-2 col-md-2">
<button type="button" class="btn btn-default" data-toggle="modal"
aria-label="New Directory" data-target="#btn-create-directory"
title="Create Directory" id="btn-create-dir">
<span class="glyphicon glyphicon-folder-open"></span>
</button>
<button type="button" class="btn btn-default" data-toggle="modal"
data-target="#modal-upload-file" title="Upload Files" id="btn-upload-files">
<span class="glyphicon glyphicon-cloud-upload"></span>
</button>
<button class="btn btn-default dropdown-toggle" type="button"
data-toggle="dropdown" title="Cut & Paste">
<span class="glyphicon glyphicon-list-alt"></span></button>
<ul class="dropdown-menu cut-paste">
<li><a id="explorer-cut">Cut</a></li>
<li><a id="explorer-paste">Paste</a></li>
</ul>
</div>
</div>
<br />
<div id="panel"></div>
<div class="row">
<hr />
<div class="col-xs-2"><p>Hadoop, {release-year-token}.</p></div>
</div>
</div>
<script type="text/x-template" id="explorer-popover-perm-info">
<div class="explorer-popover-perm-body">
<table class="table table-striped">
<thead>
<tr>
<th class="text-center">User</th>
<th class="text-center">Group</th>
<th class="text-center">Other</th>
</tr>
</thead>
<tbody>
<tr>
<td><label><input type="checkbox" data-bit="8" /> Read</label></td>
<td><label><input type="checkbox" data-bit="5" /> Read</label></td>
<td><label><input type="checkbox" data-bit="2" /> Read</label></td>
</tr>
<tr>
<td><label><input type="checkbox" data-bit="7" /> Write</label></td>
<td><label><input type="checkbox" data-bit="4" /> Write</label></td>
<td><label><input type="checkbox" data-bit="1" /> Write</label></td>
</tr>
<tr>
<td><label><input type="checkbox" data-bit="6" /> Execute</label></td>
<td><label><input type="checkbox" data-bit="3" /> Execute</label></td>
<td><label><input type="checkbox" data-bit="0" /> Execute</label></td>
</tr>
</tbody>
</table>
<div style="text-align: right; margin-right: 10px">
<label><input type="checkbox" id="explorer-perm-sticky" data-bit="9" /> Sticky bit</label>
</div>
<hr/>
<div style="text-align: right">
<button type="button" class="btn" id="explorer-perm-cancel">Cancel</button>
<button type="button" class="btn btn-success" id="explorer-set-perm-button"
data-complete-text="Updating...">Set</button>
</div>
</div>
</script>
<script type="text/x-dust-template" id="tmpl-explorer">
<table class="table" id="table-explorer">
<thead>
<tr>
<th><input type="checkbox" id="file-selector-all"></th>
<th title="Permissions">Permission</th>
<th>Owner</th>
<th>Group</th>
<th>Size</th>
<th title="Last Modified">Last Modified</th>
<th title="Replication">Replication</th>
<th>Block Size</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
{#FileStatus}
<tr inode-path="{pathSuffix}" data-permission="{permission}"
class="explorer-entry">
<td><input type="checkbox" class="file_selector"> </td>
<td><span class="explorer-perm-links editable-click">
{type|helper_to_directory}{permission|helper_to_permission}
{aclBit|helper_to_acl_bit}
</span></td>
<td><span class="explorer-owner-links">{owner}</span></td>
<td><span class="explorer-group-links">{group}</span></td>
<td>{length}</td>
<td>{modificationTime}</td>
<td><span class="explorer-replication-links">{replication}</span></td>
<td>{blockSize|fmt_bytes}</td>
<td><a inode-type="{type}" class="explorer-browse-links">{pathSuffix}</a></td>
<td><span class="glyphicon glyphicon-trash"></span></td>
</tr>
{/FileStatus}
</tbody>
</table>
</script>
<script type="text/x-dust-template" id="tmpl-block-info">
{#block}
<p>Block ID: {blockId}</p>
<p>Block Pool ID: {blockPoolId}</p>
<p>Generation Stamp: {generationStamp}</p>
<p>Size: {numBytes}</p>
{/block}
<p>Availability:
<ul>
{#locations}
<li>{hostName}</li>
{/locations}
</ul>
</p>
</script>
<script type="text/javascript" src="/static/jquery-3.4.1.min.js">
</script><script type="text/javascript" src="/static/jquery.dataTables.min.js">
</script><script type="text/javascript" src="/static/bootstrap-3.4.1/js/bootstrap.min.js">
</script><script type="text/javascript" src="/static/bootstrap-3.4.1/js/bootstrap-editable.min.js">
</script><script type="text/javascript" src="/static/dataTables.bootstrap.js">
</script><script type="text/javascript" src="/static/dust-full-2.0.0.min.js">
</script><script type="text/javascript" src="/static/dust-helpers-1.1.1.min.js">
</script><script type="text/javascript" src="/static/dfs-dust.js">
</script><script type="text/javascript" src="/static/json-bignum.js">
</script><script type="text/javascript" src="/static/rest-csrf.js">
</script><script type="text/javascript" src="explorer.js">
</script><script type="text/javascript" src="/static/moment.min.js">
</script>
</body>
</html>

View File

@ -0,0 +1,552 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
"use strict";
// The chunk size of tailing the files, i.e., how many bytes will be shown
// in the preview.
var TAIL_CHUNK_SIZE = 32768;
//This stores the current directory which is being browsed
var current_directory = "";
function show_err_msg(msg) {
$('#alert-panel-body').html(msg);
$('#alert-panel').show();
}
$(window).bind('hashchange', function () {
$('#alert-panel').hide();
var dir = decodeURIComponent(window.location.hash.slice(1));
if(dir == "") {
dir = "/";
}
if(current_directory != dir) {
browse_directory(dir);
}
});
function network_error_handler(url) {
return function (jqxhr, text, err) {
switch(jqxhr.status) {
case 401:
var msg = '<p>Authentication failed when trying to open ' + url + ': Unauthorized.</p>';
break;
case 403:
if(jqxhr.responseJSON !== undefined && jqxhr.responseJSON.RemoteException !== undefined) {
var msg = '<p>' + jqxhr.responseJSON.RemoteException.message + "</p>";
break;
}
var msg = '<p>Permission denied when trying to open ' + url + ': ' + err + '</p>';
break;
case 404:
var msg = '<p>Path does not exist on HDFS or WebHDFS is disabled. Please check your path or enable WebHDFS</p>';
break;
default:
var msg = '<p>Failed to retrieve data from ' + url + ': ' + err + '</p>';
}
show_err_msg(msg);
};
}
function append_path(prefix, s) {
var l = prefix.length;
var p = l > 0 && prefix[l - 1] == '/' ? prefix.substring(0, l - 1) : prefix;
return p + '/' + s;
}
function get_response(data, type) {
return data[type] !== undefined ? data[type] : null;
}
function get_response_err_msg(data) {
return data.RemoteException !== undefined ? data.RemoteException.message : "";
}
function delete_path(inode_name, absolute_file_path) {
$('#delete-modal-title').text("Delete - " + inode_name);
$('#delete-prompt').text("Are you sure you want to delete " + inode_name
+ " ?");
$('#delete-button').click(function() {
// DELETE /webhdfs/v1/<path>?op=DELETE&recursive=<true|false>
var url = '/webhdfs/v1' + encode_path(absolute_file_path) +
'?op=DELETE' + '&recursive=true';
$.ajax(url,
{ type: 'DELETE'
}).done(function(data) {
browse_directory(current_directory);
}).fail(network_error_handler(url)
).always(function() {
$('#delete-modal').modal('hide');
$('#delete-button').button('reset');
});
})
$('#delete-modal').modal();
}
/* This method loads the checkboxes on the permission info modal. It accepts
* the octal permissions, eg. '644' or '755' and infers the checkboxes that
* should be true and false
*/
function view_perm_details(e, filename, abs_path, perms) {
$('.explorer-perm-links').popover('destroy');
e.popover({html: true, content: $('#explorer-popover-perm-info').html(), trigger: 'focus'})
.on('shown.bs.popover', function(e) {
var popover = $(this), parent = popover.parent();
//Convert octal to binary permissions
var bin_perms = parseInt(perms, 8).toString(2);
bin_perms = bin_perms.length == 9 ? "0" + bin_perms : bin_perms;
parent.find('#explorer-perm-cancel').on('click', function() { popover.popover('destroy'); });
parent.find('#explorer-set-perm-button').off().click(function() { set_permissions(abs_path); });
parent.find('input[type=checkbox]').each(function(idx, element) {
var e = $(element);
e.prop('checked', bin_perms.charAt(9 - e.attr('data-bit')) == '1');
});
})
.popover('show');
}
// Use WebHDFS to set permissions on an absolute path
function set_permissions(abs_path) {
var p = 0;
$.each($('.popover .explorer-popover-perm-body input:checked'), function(idx, e) {
p |= 1 << (+$(e).attr('data-bit'));
});
var permission_mask = p.toString(8);
// PUT /webhdfs/v1/<path>?op=SETPERMISSION&permission=<permission>
var url = '/webhdfs/v1' + encode_path(abs_path) +
'?op=SETPERMISSION' + '&permission=' + permission_mask;
$.ajax(url, { type: 'PUT'
}).done(function(data) {
browse_directory(current_directory);
}).fail(network_error_handler(url))
.always(function() {
$('.explorer-perm-links').popover('destroy');
});
}
function encode_path(abs_path) {
abs_path = encodeURIComponent(abs_path);
var re = /%2F/g;
return abs_path.replace(re, '/');
}
function view_file_details(path, abs_path) {
function show_block_info(blocks) {
var menus = $('#file-info-blockinfo-list');
menus.empty();
menus.data("blocks", blocks);
menus.change(function() {
var d = $(this).data('blocks')[$(this).val()];
if (d === undefined) {
return;
}
dust.render('block-info', d, function(err, out) {
$('#file-info-blockinfo-body').html(out);
});
});
for (var i = 0; i < blocks.length; ++i) {
var item = $('<option value="' + i + '">Block ' + i + '</option>');
menus.append(item);
}
menus.change();
}
abs_path = encode_path(abs_path);
var url = '/webhdfs/v1' + abs_path + '?op=GET_BLOCK_LOCATIONS';
$.ajax({url: url, dataType: 'text'}).done(function(data_text) {
var data = JSONParseBigNum(data_text);
var d = get_response(data, "LocatedBlocks");
if (d === null) {
show_err_msg(get_response_err_msg(data));
return;
}
$('#file-info-tail').hide();
$('#file-info-title').text("File information - " + path);
var download_url = '/webhdfs/v1' + abs_path + '?op=OPEN';
$('#file-info-download').attr('href', download_url);
var processPreview = function(url) {
url += "&noredirect=true";
if(request && request.readyState != 4){
request.abort();
}
request = $.ajax({
cache: false,
type: 'GET',
url: url,
async: false,
processData: false,
crossDomain: true
}).done(function(data, textStatus, jqXHR) {
url = data.Location;
$.ajax({
cache: false,
type: 'GET',
url: url,
async: false,
processData: false,
crossDomain: true
}).always(function(data, textStatus, jqXHR) {
$('#file-info-preview-body').val(jqXHR.responseText);
$('#file-info-tail').show();
}).fail(function(jqXHR, textStatus, errorThrown) {
show_err_msg("Couldn't preview the file. " + errorThrown);
});
}).fail(function(jqXHR, textStatus, errorThrown) {
show_err_msg("Couldn't find datanode to read file from. " + errorThrown);
});
}
var request = null;
$('#file-info-preview-tail')
.off('click')
.on('click', function() {
var offset = d.fileLength - TAIL_CHUNK_SIZE;
var url = offset > 0 ? download_url + '&offset=' + offset : download_url;
processPreview(url);
});
$('#file-info-preview-head')
.off('click')
.on('click', function() {
var url = d.fileLength > TAIL_CHUNK_SIZE ? download_url + '&length=' + TAIL_CHUNK_SIZE : download_url;
processPreview(url);
});
if (d.fileLength > 0) {
show_block_info(d.locatedBlocks);
$('#file-info-blockinfo-panel').show();
} else {
$('#file-info-blockinfo-panel').hide();
}
$('#file-info').modal();
}).fail(network_error_handler(url));
}
/**Use X-editable to make fields editable with a nice UI.
* elementType is the class of element(s) you want to make editable
* op is the WebHDFS operation that will be triggered
* parameter is (currently the 1) parameter which will be passed along with
* the value entered by the user
*/
function makeEditable(elementType, op, parameter) {
$(elementType).each(function(index, value) {
$(this).editable({
url: function(params) {
var inode_name = $(this).closest('tr').attr('inode-path');
var absolute_file_path = append_path(current_directory, inode_name);
var url = '/webhdfs/v1' + encode_path(absolute_file_path) + '?op=' +
op + '&' + parameter + '=' + encodeURIComponent(params.value);
return $.ajax(url, { type: 'PUT', })
.fail(network_error_handler(url))
.done(function() {
browse_directory(current_directory);
});
},
error: function(response, newValue) {return "";}
});
});
}
function func_size_render(data, type, row, meta) {
if(type == 'display') {
return dust.filters.fmt_bytes(data);
}
else return data;
}
// Change the format of date-time depending on how old the
// the timestamp is. If older than 6 months, no need to be
// show exact time.
function func_time_render(data, type, row, meta) {
if(type == 'display') {
var cutoff = moment().subtract(6, 'months').unix() * 1000;
if(data < cutoff) {
return moment(Number(data)).format('MMM DD YYYY');
} else {
return moment(Number(data)).format('MMM DD HH:mm');
}
}
return data;
}
function browse_directory(dir) {
var HELPERS = {
'helper_date_tostring' : function (chunk, ctx, bodies, params) {
var value = dust.helpers.tap(params.value, chunk, ctx);
return chunk.write('' + moment(Number(value)).format('ddd MMM DD HH:mm:ss ZZ YYYY'));
}
};
var url = '/webhdfs/v1' + encode_path(dir) + '?op=LISTSTATUS';
$.get(url, function(data) {
var d = get_response(data, "FileStatuses");
if (d === null) {
show_err_msg(get_response_err_msg(data));
return;
}
current_directory = dir;
$('#directory').val(dir);
window.location.hash = dir;
var base = dust.makeBase(HELPERS);
dust.render('explorer', base.push(d), function(err, out) {
$('#panel').html(out);
$('.explorer-browse-links').click(function() {
var type = $(this).attr('inode-type');
var path = $(this).closest('tr').attr('inode-path');
var abs_path = append_path(current_directory, path);
if (type == 'DIRECTORY') {
browse_directory(abs_path);
} else {
view_file_details(path, abs_path);
}
});
//Set the handler for changing permissions
$('.explorer-perm-links').click(function() {
var filename = $(this).closest('tr').attr('inode-path');
var abs_path = append_path(current_directory, filename);
var perms = $(this).closest('tr').attr('data-permission');
view_perm_details($(this), filename, abs_path, perms);
});
makeEditable('.explorer-owner-links', 'SETOWNER', 'owner');
makeEditable('.explorer-group-links', 'SETOWNER', 'group');
makeEditable('.explorer-replication-links', 'SETREPLICATION', 'replication');
$('.explorer-entry .glyphicon-trash').click(function() {
var inode_name = $(this).closest('tr').attr('inode-path');
var absolute_file_path = append_path(current_directory, inode_name);
delete_path(inode_name, absolute_file_path);
});
$('#file-selector-all').click(function() {
$('.file_selector').prop('checked', $('#file-selector-all')[0].checked );
});
//This needs to be last because it repaints the table
$('#table-explorer').dataTable( {
'lengthMenu': [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
'columns': [
{ 'orderable' : false }, //select
{'searchable': false }, //Permissions
null, //Owner
null, //Group
{ 'searchable': false, 'render': func_size_render}, //Size
{ 'searchable': false, 'render': func_time_render}, //Last Modified
{ 'searchable': false }, //Replication
null, //Block Size
null, //Name
{ 'orderable' : false } //Trash
],
"deferRender": true
});
});
}).fail(network_error_handler(url));
}
function init() {
dust.loadSource(dust.compile($('#tmpl-explorer').html(), 'explorer'));
dust.loadSource(dust.compile($('#tmpl-block-info').html(), 'block-info'));
var b = function() { browse_directory($('#directory').val()); };
$('#btn-nav-directory').click(b);
//Also navigate to the directory when a user presses enter.
$('#directory').on('keyup', function (e) {
if (e.which == 13) {
browse_directory($('#directory').val());
}
});
var dir = window.location.hash.slice(1);
if(dir == "") {
window.location.hash = "/";
} else {
browse_directory(dir);
}
}
$('#btn-create-directory').on('show.bs.modal', function(event) {
$('#new_directory_pwd').text(current_directory);
});
$('#btn-create-directory-send').click(function () {
$(this).prop('disabled', true);
$(this).button('complete');
var url = '/webhdfs/v1' + encode_path(append_path(current_directory,
$('#new_directory').val())) + '?op=MKDIRS';
$.ajax(url, { type: 'PUT' }
).done(function(data) {
browse_directory(current_directory);
}).fail(network_error_handler(url)
).always(function() {
$('#btn-create-directory').modal('hide');
$('#btn-create-directory-send').button('reset');
});
})
$('#btn-upload-files').click(function() {
$('#modal-upload-file-button').prop('disabled', true).button('reset');
$('#modal-upload-file-input').val(null);
});
$('#btn-create-dir').click(function() {
$('#btn-create-directory-send').prop('disabled', true).button('reset');
$('#new_directory').val(null);
});
$('#modal-upload-file-input').change(function() {
if($('#modal-upload-file-input').prop('files').length >0) {
$('#modal-upload-file-button').prop('disabled', false);
}
else {
$('#modal-upload-file-button').prop('disabled', true);
}
});
$('#new_directory').on('keyup keypress blur change',function() {
if($('#new_directory').val() == '' || $('#new_directory').val() == null) {
$('#btn-create-directory-send').prop('disabled', true);
}
else {
$('#btn-create-directory-send').prop('disabled', false);
}
});
$('#modal-upload-file-button').click(function() {
$(this).prop('disabled', true);
$(this).button('complete');
var files = []
var numCompleted = 0
for(var i = 0; i < $('#modal-upload-file-input').prop('files').length; i++) {
(function() {
var file = $('#modal-upload-file-input').prop('files')[i];
var url = '/webhdfs/v1' + encode_path(append_path(current_directory, file.name));
url += '?op=CREATE&noredirect=true';
files.push( { file: file } )
files[i].request = $.ajax({
type: 'PUT',
url: url,
processData: false,
crossDomain: true
});
})()
}
for(var f in files) {
(function() {
var file = files[f];
file.request.done(function(data) {
var url = data['Location'];
$.ajax({
type: 'PUT',
url: url,
data: file.file,
processData: false,
crossDomain: true
}).always(function(data) {
numCompleted++;
if(numCompleted == files.length) {
reset_upload_button();
browse_directory(current_directory);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
numCompleted++;
reset_upload_button();
show_err_msg("Couldn't upload the file " + file.file.name + ". "+ errorThrown);
});
}).fail(function(jqXHR, textStatus, errorThrown) {
numCompleted++;
reset_upload_button();
show_err_msg("Couldn't find datanode to write file. " + errorThrown);
});
})();
}
});
//Reset the upload button
function reset_upload_button() {
$('#modal-upload-file').modal('hide');
$('#modal-upload-file-button').button('reset');
}
//Store the list of files which have been checked into session storage
function store_selected_files(current_directory) {
sessionStorage.setItem("source_directory", current_directory);
var selected_files = $("input:checked.file_selector");
var selected_file_names = new Array();
selected_files.each(function(index) {
selected_file_names[index] = $(this).closest('tr').attr('inode-path');
})
sessionStorage.setItem("selected_file_names", JSON.stringify(selected_file_names));
alert("Cut " + selected_file_names.length + " files/directories");
}
//Retrieve the list of files from session storage and rename them to the current
//directory
function paste_selected_files() {
var files = JSON.parse(sessionStorage.getItem("selected_file_names"));
var source_directory = sessionStorage.getItem("source_directory");
$.each(files, function(index, value) {
var url = "/webhdfs/v1"
+ encode_path(append_path(source_directory, value))
+ '?op=RENAME&destination='
+ encode_path(append_path(current_directory, value));
$.ajax({
type: 'PUT',
url: url
}).done(function(data) {
if(index == files.length - 1) {
browse_directory(current_directory);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
show_err_msg("Couldn't move file " + value + ". " + errorThrown);
});
})
}
$('#explorer-cut').click(function() {
store_selected_files(current_directory);
});
$('#explorer-paste').click(function() {
paste_selected_files();
});
init();
})();

View File

@ -41,9 +41,12 @@
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Utilities <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="explorer.html">Browse the file system</a></li>
<li><a href="logs">Logs</a></li>
<li><a href="logLevel">Log Level</a></li>
<li><a href="jmx">Metrics</a></li>
<li><a href="conf">Configuration</a></li>
<li><a href="logs">Logs</a></li>
<li><a href="stacks">Process Thread Dump</a></li>
</ul>
</li>
</ul>