【问题标题】:Node.js Server: Image Upload / Corruption IssuesNode.js 服务器:图像上传/损坏问题
【发布时间】:2014-10-19 22:59:35
【问题描述】:

所以我尝试在 Node.js 中编写一个基本的文件服务器,而我尝试上传和存储在其上的所有图像都以损坏的形式返回。这个问题似乎与节点缓冲区处理转换为 UTF-8 并再次转换回来的方式有关(我必须这样做才能将 POST 正文标头从二进制数据中取出)。

这是一个简单的 Node 服务器,显示了我当前的方法和我遇到的问题:

var http = require('http');

var server = http.createServer(function(request, response) {
    if (request.method === "GET") {
        // on GET request, output a simple web page with a file upload form
        var mypage = '<!doctype html><html><head><meta charset="utf-8">' + 
                        '<title>Submit POST Form</title></head>\r\n<body>' +
                        '<form action="http://127.0.0.1:8008" method="POST" ' + 
                        'enctype="multipart/form-data"> <input name="upload" ' + 
                        'type="file"><p><button type="submit">Submit</button>' + 
                        '</p></form></body></html>\r\n';
        response.writeHead(200, {
            "Content-Type": "text/html",
            "Content-Length": mypage.length
        });
        response.end(mypage);

    } else if (request.method === "POST") {
        // if we have a return post request, let's capture it
        var upload = new Buffer([]);

        // get the data
        request.on('data', function(chunk) {
            // copy post data 
            upload = Buffer.concat([upload, chunk]);
        });

        // when we have all the data
        request.on('end', function() {
            // convert to UTF8 so we can pull out the post headers
            var str = upload.toString('utf8');
            // get post headers with a regular expression
            var re = /(\S+)\r\nContent-Disposition:\s*form-data;\s*name="\w+";\s*filename="[^"]*"\r\nContent-Type: (\S+)\r\n\r\n/i,
                reMatch = str.match(re);
            var lengthOfHeaders = reMatch[0].length,
                boundary = reMatch[1],
                mimeType = reMatch[2];
            // slice headers off top of post body
            str = str.slice(lengthOfHeaders);
            // remove the end boundary
            str = str.replace("\r\n" + boundary + "--\r\n", '');
            // convert back to buffer
            var rawdata = new Buffer(str, 'utf8');
            // echo back to client
            response.writeHead(200, {
                "Content-Type": mimeType
            });
            response.end(rawdata);
        });
    }
});

server.listen(8008);
console.log("server running on port 8008");

要对其进行测试,请在节点中运行脚本并在浏览器中转到 127.0.0.1:8008。尝试上传图片并提交表单。图像每次都以损坏的形式返回 - 即使脚本应该直接将图像数据回显到浏览器。

那么有谁知道我在这里做错了什么?有没有更好的方法来处理我还没有想到的 Node 中的 POST 正文标头? (在任何人说什么之前,不,我不想想使用 Express。我想弄清楚并理解这个问题。)

【问题讨论】:

    标签: javascript node.js utf-8


    【解决方案1】:

    问题似乎与节点缓冲区处理转换为 UTF-8 并再次转换回来的方式有关

    我想你是对的,转换为 UTF-8 是一个坏主意,但可以这样做只是为了处理文件并获取标题和边界位置,但保持缓冲区文件不变,当你有从文件中获取标题和边界的所有位置只需将缓冲区复制到这样的新缓冲区

    originalBuffer.copy(newBuffer,0, positionHeader, positionEndBoundary)

    var http = require('http');
    var fs = require('fs');
    var connections = 0;
    
    var server = http.createServer(function (req, res) {
    connections++;
    console.log(req.url,"connections: "+connections);
    if(req.url == '/'){
        res.writeHead(200, { 'content-type': 'text/html' });
        res.end(
            '<form action="/upload" enctype="multipart/form-data" method="post">' +
            '<input type="file" name="upload" multiple="multiple"><br>' +
            '<input type="submit" value="Upload">' +
            '</form>'
        );
    }
    
    var body = new Buffer([]);
    if (req.url == '/upload') {
        req.on('data', function (foo) {
    
            //f.write(foo);
            body = Buffer.concat([body,foo]);
            if(isImage(body.toString())){
                console.log("é imagem do tipo "+isImage(body.toString()));
            }
            else{
                console.log("Não é imagem");
                res.end("Não é imagem");
            }
            console.log(body.length, body.toString().length);
        });
        req.on('end', function () {
            // console.log(req.headers);
            //I converted the buffer to "utf 8" but i kept the original buffer
            var str = body.toString();
            console.log(str.length);
            imageType = isImage(body.toString());
            //get the index of the last header character
            //I'm just using the string to find the postions to cut the headers and boundaries
            var index = str.indexOf(imageType)+(imageType+"\r\n\r\n").length;
            // var headers= str.slice(0,index).split(';');
            // console.log(headers);
    
            //Here comes the trick
            /*
            *I have to cut the last boundaries, so i use the lastIndexOf to cut the second boundary
            * And maybe that is the corruption issues, because, I'm not sure, but I guess
            * the UTF-8 format only use 7bits to represent all characters, and the buffer can use 8bits, or two hex,
            *So, i need to take the difference here (body.length-str.length)  
            */ 
            var indexBoundayToBuffer = str.lastIndexOf('------WebKitFormBoundary')+(body.length-str.length);
            console.log(index, indexBoundayToBuffer);
            //maybe you can change this to use less memory, whatever
            var newBuffer = Buffer.alloc(body.length);
            /*
            *And now use the index, and the indexBoudayToBuffer and you will have only the binary
            */
            body.copy(newBuffer,0,index,indexBoundayToBuffer);
    
            // f.end();
            //file type
            var type = imageType.substr("image/".length);
            console.log("END");
            fs.writeFile("nameFile."+type,newBuffer,function(err,ok){
                if(err){
                    console.log(err);
                    return false;
                }
                res.end();
    
            });
        });
    }
    
    });
    
    function isImage(str){
    
    if(str.indexOf('image/png')!=-1) return 'image/png';
    else if(str.indexOf('image/jpeg')!=-1) return 'image/jpeg';
    else if(str.indexOf('image/bmp'!=-1)) return 'image/bmp';
    else if(str.indexOf('image/gif'!=-1)) return 'image/gif';
    else false;
    }
    
    var port = process.env.PORT || 8080;
    server.listen(port, function () {
    console.log('Recording connections on port %s', port);
    });
    

    【讨论】:

      【解决方案2】:

      真的不应该使用这样的正则表达式来解析多部分有效负载,因为它很容易使尝试解析图像数据变得非常不可靠。 npm 上有一些模块可以为您解析表单,例如 busboymultipartyformidable。它们都不使用正则表达式,也不需要 Express。

      【讨论】:

      • 感谢您的建议。在这种情况下,Busboy 似乎可以满足我的需求。也就是说,我仍然想知道我在这里做错了什么。除了使用正则表达式之外,还有什么您认为可能导致它的原因吗?
      • 另外,刚刚在 busboy 上注册了 an issue。似乎不适合我。
      • 你没有向服务员提供任何数据。在事件处理程序之后添加request.pipe(busboy);
      • 一段时间后回到这个线程。据我记得,这确实是问题所在。哦。所以,是的,如果将来有人读这篇文章,busboy 工作得很好。
      猜你喜欢
      • 2021-08-21
      • 2014-04-26
      • 2019-05-06
      • 1970-01-01
      • 2012-08-03
      • 2020-08-22
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多