【问题标题】:Update binary files in Node.js更新 Node.js 中的二进制文件
【发布时间】:2020-02-15 21:48:47
【问题描述】:

我正在尝试使用 Node.js 处理二进制文件。

我正在尝试从客户端接收二进制文件,打开二进制文件,转换为十六进制,替换数据并将新的二进制文件返回给客户端。

app.use('/read-binary-file',(req,res) => {
    try {
        let content = fs.readFileSync(file_path);
        console.log('content', content)
        res.status(200).send({content})
    }
    catch(err) {
        console.error(err);
    }
})

我编写了获取现有文件并尝试读取它的代码。当我打印它时,我会在缓冲区中得到它:

content <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >

我不确定如何将其转换为十六进制,然后尝试更改它... 当我通过在线十六进制编辑器读取文件时,我以每行 16 位的形式获取它们,这种方式非常舒适。

我有一些问题:

  1. 如何从二进制转换为十六进制?
  2. 如何替换十六进制文件中的数据,然后返回给客户端?
  3. 如何在代码中将它们显示为 16 位?
  4. 将它们存储在数据库中的最佳方式是什么?存储文件,然后 在数据库中只存储路径?

是否有任何文档可以提供帮助?

【问题讨论】:

    标签: javascript node.js file express binaryfiles


    【解决方案1】:
    1. 从二进制转换为十六进制:

      const file = fs.readFileSync("./test");
      const str = file.toString("hex");
      
    2. 替换数据并返回给客户端:

      let newStr = str.replace( "00", "FF" );
      let buffer = Buffer.from( newStr, "hex" );
      
    3. 以 16 位显示:

      for ( let i = 0; i < newStr.length; i+=16 ){
        console.log( newStr.slice( i, i+16 ) + "\n" );
      }
      
    4. 将文件存储在可写的上传文件夹中,并将上传的URL路径存储在数据库中。这是我的建议,根据我的经验。您可以在这篇 Quora 帖子中详细了解是否要选择将图像存储在数据库中(BLOB 格式):Is it a bad design to store images as blobs in a database?


    这是一个可能对您有所帮助的基本设置:

    /测试

    ABC
    

    /app.js

    const express = require('express');
    const app = express();
    const fs = require('fs');
    
    app.use("/test", (req, res) => {
    
        const file = fs.readFileSync("./test");     // Read file as binary
        const str = file.toString("hex");           // Convert to hexadecimal
        let newStr = str.replace(/41|43/g, "42");   // Replace hexadecimal characters
        let buffer = Buffer.from(newStr, "hex");    // Create buffer from hexadecimal
    
        // Send to the user as download
        res.setHeader('Content-disposition', 'attachment; filename=test-edited.txt');
        res.setHeader('Content-type', 'text/plain');
        res.charset = 'UTF-8';
        res.write(buffer);
        res.end();
    });
    
    app.listen(3000, () => console.log('Server Running...'));
    

    测试文件包含字符 ABC。它们被转换为 BBB,然后被下载。

    您可以通过设置适当的文件名和MIME type(内容类型)来选择输出不同的文件类型,例如用于下载PNG 图像:

        res.setHeader('Content-disposition', 'attachment; filename=output.png');
        res.setHeader('Content-type', 'image/png');
    

    注意:对于直接二进制操作,无需中间的十六进制转换,请参阅Christos Lytras' answer

    【讨论】:

    • 你好,如何将字符串转回文件并作为二进制文件返回给客户端?
    • 您需要在下面的链接中阅读更多相关信息。猜测您正在使用 ExpressJS,因此这些链接将帮助您入门。您可以选择保存文件并提供它或动态创建它并将其流式传输到客户端。您可以使用任何您想要的 MIME 类型,而不仅仅是文本/html 或示例中的纯文本。 stackoverflow.com/questions/7288814/…stackoverflow.com/questions/18467620/…gist.github.com/davidbanham/1186032
    • 对于动态生成的文件:medium.com/@Shekharrajak/…
    • @Manspof 我已经用一个简单的设置更新了答案来帮助你。
    • 嗨,kostas,你能解释一下如何使用二进制文件吗?你写的是文本/普通文件
    【解决方案2】:

    您无需将二进制数据转换为十六进制即可替换其中的任何内容。如果要替换二进制文件中的数据,则意味着您确切地知道要替换的内容,这意味着基于某些模式或数据结构或字节块的特定偏移量,您必须搜索。

    如果您想为您的客户创建一个在线十六进制编辑器,那是完全不同的事情,它不仅涉及后端逻辑,还涉及允许用户上传/编辑和下载文件的 UI。

    使用Buffer.copy将二进制数据替换为固定偏移量上的二进制数据:

    // The first three bytes of data Buffer will be replaced with 0x02 0x03 0x04 bytes
    Buffer.alloc(3, new Uint8Array([0x02, 0x03, 0x04])).copy(data, 0, 0, 3);
    

    使用Buffer.indexOf 搜索二进制数据模式:

    // Loading a PNG image
    const data = fs.readFileSync('js-logo-16x16.png');
    
    // Search for the PNG signature using Buffer.indexOf
    const sigpos = data.indexOf(Buffer.from('PNG'));
    
    if (sigpos >= 0) {
      // If signature pos found (!= -1), replace it with JPG using Buffer.write
      data.write('JPG', sigpos, 3);
    }
    

    您可以使用简单的循环和字符串操作逻辑来打印十六进制数据:

    // For Node.js using a Buffer
    function displayHexData(data) {
      for (let addr = 0; addr <= data.length; addr += 16) {
        const displayAddr = addr.toString(16).toUpperCase().padStart(8, '0');
        const block = data.slice(addr, Math.min(addr + 16, data.length));
    
        let hexblock = block.toString('hex');
    
        if (addr + 16 > data.length) {
          hexblock += '  '.repeat(16 - (data.length - addr));
        }
    
        const charsblock = Array.from(block)
          .map(b => b >= 32 && b <= 126 ? String.fromCharCode(b) : '.')
          .join('');
    
        console.log(displayAddr, hexblock.split(/(.{2})/).join(' '), charsblock);
      }
    }
    

    将输出如下内容:

    对于网页 JavaScript:

    // 16 x 16 PNG 8-bit
    const base64Data = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAA3NCSVQICAjb4U/gAAAAMFBMVEUAAADQvhqQghFBOgj/7iAjIAT84x+6qxdqYA3u1x2nlxT//CIxLAZKQwnOuhnZyBvQr3QtAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAAFNJREFUCJljSIMCBlIZKW4ubmBGhkrtdTDD1Yz5rBuI4XOTyQUs4mPdEA4SSfGx3n7gCZDxKfV+24fVQEYBQyED6zQgI39d2qyVIMUpW9Kyt6UBAGorNUfBuVldAAAAAElFTkSuQmCC';
    
    const data = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0));
    
    displayHexData(data);
    
    function displayHexData(data) {
      let result = '';
    
      for (let addr = 0; addr <= data.length; addr += 16) {
        const displayAddr = addr.toString(16).toUpperCase().padStart(8, '0');
        const block = data.slice(addr, Math.min(addr + 16, data.length));
    
        let hexblock = Array.from (block)
          .map (b => b.toString(16).toUpperCase().padStart(2, "0"))
          .join('');
    
        if (addr + 16 > data.length) {
          hexblock += '  '.repeat(16 - (data.length - addr));
        }
    
        const charsblock = Array.from(block)
          .map(b => b >= 32 && b <= 126 ? String.fromCharCode(b) : '.')
          .join('');
    
        result += `${displayAddr} ${hexblock.split(/(.{2})/).join(' ')} ${charsblock}\n`;
      }
    
      document.getElementById('hex').appendChild(document.createTextNode(result));
    }
    #hex {
      font-size: small;
    }
    &lt;pre id="hex"&gt;&lt;/pre&gt;

    至于将二进制数据保存到数据库,当然取决于数据库类型。大多数数据库都支持 Binary Large OBject (BLOB) 数据类型,例如 MySQL,您可以使用它来存储二进制数据。将文件保存到文件系统并仅使用字段来保存其路径也是一种不错的方法,但您必须处理备份/恢复,包括将存储文件的目录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-19
      • 2012-06-18
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 2013-12-06
      • 1970-01-01
      • 2016-12-19
      相关资源
      最近更新 更多