在GitHub中列出文件大小

24

GitHub文件浏览器列出了文件名和有关最后一次提交的信息:

GitHub文件浏览器

是否有办法在这些列表中添加每个文件的大小?


2
一个关于它的功能请求:https://github.com/isaacs/github/issues/622 - Ciro Santilli OurBigBook.com
5个回答

43
如果没有官方方法可以实现这一点,这里有一个书签工具,使用GitHub的API添加大小信息(在IE中不起作用):
如果没有官方方法可以实现这一点,这里有一个书签工具,使用GitHub的API添加大小信息(在IE中不起作用):
javascript:(function(){function f(a){var g=document.querySelector('div[role="rowheader"] a[title="'+a.name+'"]').closest('div[role="row"]').lastElementChild,c=document.createElement("div");c.style.width="5em";"file"===a.type&&(c.textContent=(a.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",c.style.textAlign="right",c.style.whiteSpace="nowrap");g.insertAdjacentElement("beforebegin",c)}var b=window.location.pathname.split("/"),d=b[1],h=b[2],e=b[4];b=b.slice(5);d=["https://api.github.com/repos",d,h,"contents"].concat(b||[]).join("/")+(e?"?ref="+e:"");console.log(d);fetch(d).then(function(a){return a.json()}).then(function(a){return Array.isArray(a)?a.forEach(f):console.warn(a)})})();

文件大小已添加

未压缩源代码:

(function () {
    "use strict";

    //Parse the current GitHub repo url. Examples:
    //  Repo root:           /Sphinxxxx/vanilla-picker
    //  Subfolder:           /Sphinxxxx/vanilla-picker/tree/master/src/css
    //  Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
    //  Subfolder at tag:    /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
    //
    //If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
    //Here, we put that in the "ref" variable:
    const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/');

    //Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
    //Example:
    //  https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22
    const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
          url = query.join('/') + (ref ? '?ref=' + ref : '');
    console.log(url);

    //https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
    fetch(url).then(r => r.json())
              .then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn(j));

    function handleFileInfo(info) {
        //console.log(info);
        const link = document.querySelector(`div[role="rowheader"] a[title="${info.name}"]`);
        const timeCol = link.closest('div[role="row"]').lastElementChild;

        const sizeCol = document.createElement('div');
        sizeCol.style.width = '5em';
        if(info.type === 'file') {
            //https://dev59.com/P3E85IYBdhLWcg3wMAfc#17663871
            //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
            sizeCol.textContent = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
            sizeCol.style.textAlign = 'right';
            sizeCol.style.whiteSpace = 'nowrap';
        }

        timeCol.insertAdjacentElement('beforebegin', sizeCol);
    }
})();

从第一段代码创建一个书签脚本,或者只需将其复制粘贴到您的浏览器控制台中。


不错。+1。我希望这不会给API带来负担,我知道有调用速率限制,所以如果你浏览太多...那行不通。 - VonC
@VonC - 很好的观点!看起来限制是每小时60个请求(没有大小限制):https://developer.github.com/v3/#rate-limiting - Sphinxxx
它适用于私有仓库吗?对我来说,它出现了404 XHR错误。 - Raymond
2
@RaymondPeng - 通过API访问私有仓库可能需要身份验证,而这个书签工具并没有提供此功能:https://developer.github.com/v3/#authentication - Sphinxxx
谢谢!你能更新到最新的Github布局吗?它似乎不再起作用了。 - d33tah
@d33tah - 已更新 - Sphinxxx


1

你可以使用调用GitHub API的JavaScript书签小工具强制文件大小显示。

2023年的GitHub现在有两种类型的文件表格(一种是基于<table>,另一种是基于<div>),因此我准备了这个新的书签小工具,在修改之前检测使用的样式:

javascript:(function e(){let t=[...document.querySelectorAll('div[role="row"]')],n;if(t.length||(t=[...document.querySelectorAll("tbody tr")],n=document.querySelector("thead tr")),(t=t.slice(1)).length&&"github.com"===document.location.host){let[,l,i,r,o,...c]=window.location.pathname.split("/");if(i&&("tree"===r||!r)){let s=["https://api.github.com/repos",l,i,"contents"].concat(c||[]).join("/")+(o?"?ref="+o:"");console.log(s),fetch(s).then(e=>e.json()).then(e=>{let l=new Map;if(e.forEach(e=>l.set(e.name,e)),n&&"Size"!==n.children[1].innerText){let i=document.createElement("th");i.style.width="6em",i.textContent="Size",n.firstElementChild.insertAdjacentElement("afterend",i)}for(let r of t){let o=r.children[n?0:1],c=o.innerText;c.indexOf("\n")>0&&(c=c.slice(0,c.indexOf("\n")));let s=l.get(c),d=document.createElement(n?"td":"div");d.style.width="6em",d.className=o.className,d.innerText=a(s?.size),o.insertAdjacentElement("afterend",d)}})}else console.error("GITHUB PATH NOT UNDERSTOOD")}else console.error("GITHUB TABLE NOT FOUND; can't add file sizes");function a(e){if(!e)return null==e?"–":"0";if(e<0)return"-"+a(-e);let t;return e<1024?e+" B":(e<1048576?(t=" KiB",e/=1024):e<1073741824?(t=" MiB",e/=1048576):(t=" GiB",e/=1073741824),e.toFixed(1)+t)}})();

请参考此处的说明制作书签。

示例输出

以下是原始未压缩代码:

(function addSizes() {
  // Sometimes each row is like `<div role="row" class="Box-row...">` with no column headers
  //   e.g. https://github.com/qwertie/ecsharp
  // Sometimes each row is like `<tr class="react-directory-row">` with header in `<thead...><tr...>`
  //   e.g. https://github.com/qwertie/ecsharp/tree/master/Main
  // Look for the first kind of rows; if not found, look for the other kind
  let rows = [...document.querySelectorAll(`div[role="row"]`)], header;
  if (!rows.length) {
    rows = [...document.querySelectorAll(`tbody tr`)];
    header = document.querySelector(`thead tr`);
  }
  rows = rows.slice(1);
    
  if (rows.length && document.location.host === 'github.com') {
    // Parse path after https://github.com, e.g.
    //     /username/repo
    //     /username/repo/tree/branch-name
    //     /username/repo/tree/branch-name/folder-name
    let [/* Leading slash */, owner, repo, __tree__, branch, ...path] = window.location.pathname.split('/');
    if (repo && (__tree__ === 'tree' || !__tree__)) {
      let query = ['https://api.github.com/repos', owner, repo, 'contents']
        .concat(path || []).join('/') + (branch ? '?ref=' + branch : '');
      console.log(query);
      // The GitHub API will returns an array of objects like
      //   [{ "name": "file.py", "path": "folder/file.py", "size": 5226, ... }, ...]
      fetch(query).then(r => r.json()).then(files => {
        // Index the results by file name
        let fileMap = new Map();
        files.forEach(file => fileMap.set(file.name, file));

        // If there is a header row, add a header cell for the file size. Note:
        // If user browses to another folder, the 'Size' header column still exists
        // but no file sizes. In that case, avoid adding another 'Size' column.
        if (header && header.children[1].innerText !== 'Size') {
            let sizeCol = document.createElement('th');
            sizeCol.style.width = '6em';
            sizeCol.textContent = 'Size';
            header.firstElementChild.insertAdjacentElement('afterend', sizeCol);
        }

        // For each row of the table: get the file name, look it up in 
        // fileMap, get the file size, and insert a size column.
        for (let row of rows) {
            let nameCol = row.children[header ? 0 : 1];
            let name = nameCol.innerText;
            if (name.indexOf('\n') > 0) name = name.slice(0, name.indexOf('\n'));
            let file = fileMap.get(name);
            let sizeCol = document.createElement(header ? 'td' : 'div');
            sizeCol.style.width = '6em';
            sizeCol.className = nameCol.className;
            sizeCol.innerText = formatFileSize(file?.size);
            nameCol.insertAdjacentElement('afterend', sizeCol);
        }
      });
    } else {
      console.error('GITHUB PATH NOT UNDERSTOOD');
    }
  } else {
    console.error("GITHUB TABLE NOT FOUND; can't add file sizes");
  }
  
  function formatFileSize(size) {
    if (!size) return size == null ? '–' : '0';
    if (size < 0) return '-' + formatFileSize(-size);
    
    let suffix;
    if (size < 1024) {
        return size + ' B';
    } else if (size < 1024*1024) {
        suffix = ' KiB';
        size /= 1024;
    } else if (size < 1024*1024*1024) {
        suffix = ' MiB';
        size /= 1024*1024;
    } else {
        suffix = ' GiB';
        size /= 1024*1024*1024;
    }
    
    return size.toFixed(1) + suffix;
  };
})();

这段内容基于@Sphinxxx的代码,但是文件大小将会成为第二列而非第三列,并且单位会随着文件大小的增加而自动更改。

0

不,GitHub文件浏览器无法进行这样的配置。

要获取额外信息意味着为GitHub传输大量额外数据(对于每个存储库的每个页面),因此我不确定您是否会很快看到此功能。


请注意,“大小”是GitHub搜索的可接受标准(这意味着大小信息存在且可以使用,不仅用于浏览文件)。

element language:xml size:100

匹配被标记为XML并且恰好有100个字节的单词“element”的代码。


4
谢谢,但我不明白那会带来“大量的额外数据”的意思。您能再详细说明一下吗? - Sphinxxx
2
@Sphinxxx 简单来说:将那些额外的位数乘以 GitHub 每天服务的页面数量,你就会得到需要传输的额外数据量(更不用提计算和缓存了)。如果想要添加任何额外信息(比如大小),这并不是一件简单的事情。如果你想了解 GitHub 的可扩展性问题,请听 http://softwareengineeringdaily.com/2016/07/26/scaling-github-with-sam-lambert/。 - VonC
7
如果你想要一个流行的网络应用程序,你需要传输信息。这就是我们支付费用的原因。我非常希望他们有比“通过网络发送文件大小信息太昂贵”的更好的理由。可以默认关闭它,然后通过打开它的人数来看有多少人需要它。当我浏览存储库时,这经常让我感到烦恼。 - Michael Terry
“巨大的额外数据量”是相对的。这些信息可以被缓存。这个功能基本上取决于GitHub。 - phil294
2
@Blauhirn 我同意,但不幸的是,在GitHub的规模下,它非常相关,而且相当巨大。此外,缓存意味着您需要维护缓存信息的一致性,考虑到GitHub托管的Git存储库本身是分布式的(通过DGit和Spoke),这并不容易。所以...确实取决于GitHub,但最好由他们来完成:这不是一个微不足道的任务。 - VonC

0

在 @Sphinxxx 的回答之后更新了脚本/书签,现在加上了注释:

书签:

!function(){"use strict";const[,t,e,,n,...i]=window.location.pathname.split("/"),o=["https://api.github.com/repos",t,e,"contents"].concat(i||[]).join("/")+(n?"?ref="+n:"");function r(t){var e=null,n=null;if("file"===t.type){const i=`div[role="rowheader"]  a[title="${t.name}"]`;e=document.querySelector(i).closest('div[role="row"]').lastElementChild,(n=document.createElement("div")).style.width="5em",n.textContent=(t.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",n.style.textAlign="right",n.style.whiteSpace="nowrap",e.insertAdjacentElement("beforebegin",n)}}fetch(o).then((t=>t.json())).then((t=>Array.isArray(t)?t.forEach(r):console.warn("Not an array of files: ",t)))}();

使用方法:

右键点击,复制链接,粘贴到地址栏(或书签链接中),在不加引号的情况下添加“javascript:”,按回车键

适用于新的 GitHub 布局,左侧为文件夹树,右侧为文件内容:

!function(){"use strict";const[,t,e,,n,...i]=window.location.pathname.split("/"),o=["https://api.github.com/repos",t,e,"contents"].concat(i||[]).join("/")+(n?"?ref="+n:"");function r(t){var e=null,n=null;if("file"===t.type){const i=`div[title="${t.name}"]`;e=document.querySelector(i).closest('tr[class="react-directory-row"]').lastElementChild,(n=document.createElement("td")).style.width="5em",n.innerHTML=(t.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",e.insertAdjacentElement("beforebegin",n)}}fetch(o).then((t=>t.json())).then((t=>Array.isArray(t)?t.forEach(r):console.warn("Not an array of files: ",t)))}();

脚本1:

(function () {
    "use strict";

    //Parse the current GitHub repo url. Examples:
    //  Repo root:           /Sphinxxxx/vanilla-picker
    //  Subfolder:           /Sphinxxxx/vanilla-picker/tree/master/src/css
    //  Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
    //  Subfolder at tag:    /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
    //
    //If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
    //Here, we put that in the "ref" variable:
    const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/'); // split url and store pieces into constants.

    //Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
    //Example:
    //  https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22;
    const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
          url = query.join('/') + (ref ? '?ref=' + ref : '');

    //https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
    fetch(url).then(r => r.json())
              .then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn("Not an array of files: ",j));

    function handleFileInfo(info) {
        var timeCol = null;
        var sizeCol = null;
        var link = "";
        var QR = "";
        if(info.type === 'file') { // skip folders
          const QR = `div[role="rowheader"]  a[title="${info.name}"]`; // select the cell containing the file name
          link = document.querySelector(QR);

          ///// Climb along the html hierarchy until it finds a DIV named "row",
          ///// and get last element (last cell of row), i.e. the date column:
          timeCol = link.closest('div[role="row"]').lastElementChild;

          ///// Create label for file size
          sizeCol = document.createElement('div');
          sizeCol.style.width = '5em';
          //https://dev59.com/P3E85IYBdhLWcg3wMAfc#17663871
          //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
          sizeCol.textContent = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB'; 
          sizeCol.style.textAlign = 'right';
          sizeCol.style.whiteSpace = 'nowrap';

          ///// Insert new label before last element of row:
          timeCol.insertAdjacentElement('beforebegin', sizeCol);
        } else {
            // skip folders
        }

    }
})();

脚本2:

(function () {
    "use strict";

    //Parse the current GitHub repo url. Examples:
    //  Repo root:           /Sphinxxxx/vanilla-picker
    //  Subfolder:           /Sphinxxxx/vanilla-picker/tree/master/src/css
    //  Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
    //  Subfolder at tag:    /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
    //
    //If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
    //Here, we put that in the "ref" variable:
    const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/'); // split url and store pieces into constants.

    //Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
    //Example:
    //  https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22;
    const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
          url = query.join('/') + (ref ? '?ref=' + ref : '');

    //https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
    fetch(url).then(r => r.json())
              .then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn("Not an array of files: ",j));

    function handleFileInfo(info) {
        var timeCol = null;
        var sizeCol = null;
        var link = "";
        var QR = "";
        if(info.type === 'file') { // skip folders
          const QR = `div[title="${info.name}"]`; // select the cell containing the file name
          link = document.querySelector(QR);

          ///// Climb along the html hierarchy until it finds a DIV named "row",
          ///// and get last element (last cell of row), i.e. the date column:
          timeCol = link.closest('tr[class="react-directory-row"]').lastElementChild;

          ///// Create label for file size
          sizeCol = document.createElement('td');
          sizeCol.style.width = '5em';
          //https://dev59.com/P3E85IYBdhLWcg3wMAfc#17663871
          //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
          sizeCol.innerHTML = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
          //sizeCol.style.textAlign = 'right';
          //sizeCol.style.whiteSpace = 'nowrap';

          ///// Insert new label before last element of row:
          timeCol.insertAdjacentElement('beforebegin', sizeCol);
        } else {
            // skip folders
        }

    }
})();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接