1

我是 node.js 的新手。我想要做的是通过我的 node.js 服务器将文件从网络浏览器上传到云存储。

我正在使用“express”、“request”和“busboy”模块。

var express = require("express");
var request = require("request");
var BusBoy = require("busboy");
var router = express.Router();

router.post("/upload", function(req, res, next) {
    var busboy = new BusBoy({ headers: req.headers });
    var json = {};

    busboy.on("file", function (fieldname, file, filename, encoding, mimetype) {
        file.on("data", function(data) {
            console.log(`streamed ${data.length}`);
        });

        file.on("end", function() {
            console.log(`finished streaming ${filename}`);
        });
        
        var r = request({
            url: "http://<my_cloud_storage_api_url>",
            method: "POST",
            headers: {
                "CUSTOM-HEADER": "Hello",
            },
            formData: {
                "upload": file
            }
        }, function(err, httpResponse, body) {
            console.log("uploaded");
            json.response = body;
        });
    });

    busboy.on("field", function(name, val) {
        console.log(`name: ${name}, value: ${value}`);
    });
    
    busboy.on("finish", function() {
        res.send(json);
    });

    req.pipe(busboy);
});

module.exports = router;

但是我在服务器上不断收到以下错误。我在这里做错了什么?任何帮助表示赞赏。

Error: Part terminated early due to unexpected end of multipart data
at node_modules\busboy\node_modules\dicer\lib\Dicer.js:65:36
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)
4

1 回答 1

1

我意识到这个问题已经有 7 个月的历史了,但我将在这里回答它,以帮助目前正在努力解决这个问题的其他人。

你有两个选择,真的:添加文件大小,或者使用 Request 以外的东西。

注意:我在第一次发布后不久对其进行了编辑,希望能提供更多的背景信息。

使用其他东西

如果您不需要它具有的所有内置功能,则可以使用一些替代方法来代替 Request。

  • form-data可以在简单的情况下单独使用,也可以与got. request在内部使用它。
  • bhttp宣传 Streams2+ 支持,但根据我的经验,Streams2+ 支持对我来说不是问题。没有内置https支持,您必须指定自定义代理
  • got另一个瘦了一个。没有像 do 那样对表单数据进行任何特殊处理request,但通常与form-dataor一起使用form-data2。不过,我很难让它通过公司代理运行,但这很可能是因为我是网络新手。
  • needle看起来很轻,但我还没有真正尝试过。

使用请求:添加文件大小

请求(截至撰写时)不支持使用transfer-encoding: chunkedso 上传文件,您需要将文件的大小与文件一起添加,如果您从 Web 客户端上传,则意味着客户端需要发送该文件除了文件本身之外,您的服务器的大小。

我想出的方法是在文件字段之前在自己的字段中发送文件元数据。

我用描述我所做的评论修改了你的例子。请注意,我没有对收到的数据进行任何验证,但我建议您添加它。

var express = require("express");
var request = require("request");
var BusBoy = require("busboy");
var router = express.Router();

router.post("/upload", function(req, res, next) {
    var busboy = new BusBoy({ headers: req.headers });
    var json = {};

    // Use this to cache any fields which are file metadata.
    var fileMetas = {};

    busboy.on("file", function (fieldname, file, filename, encoding, mimetype) {
        // Be sure to match this prop name here with the pattern you use to detect meta fields.
        var meta = fileMetas[fieldname + '.meta'];

        if (!meta) {
            // Make sure to dump the file.
            file.resume();
            // Then, do some sort of error handling here, because you cannot upload a file
            // without knowing it's length.
            return;
        }

        file.on("data", function(data) {
            console.log(`streamed ${data.length}`);
        });

        file.on("end", function() {
            console.log(`finished streaming ${filename}`);
        });

        var r = request({
            url: "http://<my_cloud_storage_api_url>",
            method: "POST",
            headers: {
                "CUSTOM-HEADER": "Hello",
            },
            formData: {
                // value + options form of a formData field.
                "upload": {
                    value: file,
                    options: {
                        filename: meta.name,
                        knownLength: meta.size
                    }
                }
            }
        }, function(err, httpResponse, body) {
            console.log("uploaded");
            json.response = body;
        });
    });

    busboy.on("field", function(name, val) {
        // Use whatever pattern you want.  I used (fileFieldName + ".meta").
        // Another good one might be ("meta:" + fileFieldName).
        if (/\.meta$/.test(name)) {
            // I send an object with { name, size, type, lastModified },
            // which are just the public props pulled off a File object.
            // Note: Should probably add error handling if val is somehow not parsable.
            fileMetas[name] = JSON.parse(val);
            console.log(`file metadata: name: ${name}, value: ${value}`);
            return;
        }

        // Otherwise, process field as normal.
        console.log(`name: ${name}, value: ${value}`);
    });

    busboy.on("finish", function() {
        res.send(json);
    });

    req.pipe(busboy);
});

module.exports = router;

在客户端上,您需要在文件本身之前在所谓的字段上发送元数据。这可以通过<input type="hidden">在文件之前订购一个控件并更新其值来完成onchange发送的值的顺序保证遵循外观中的输入顺序。如果您自己使用 构建请求正文FormData,则可以通过在附加File.

示例<form>

<script>
    function extractFileMeta(file) {
        return JSON.stringify({
            size: file.size,
            name: file.name,
            type: file.type,
            lastUpdated: file.lastUpdated
        });
    }

    function onFileUploadChange(event) {
        // change this to use arrays if using the multiple attribute on the file input.
        var file = event.target.files[0];
        var fileMetaInput = document.querySelector('input[name=fileUpload.meta]');

        if (fileMetaInput) {
            fileMetaInput.value = extractFileMeta(file);
        }
    }
</script>
<form action="/upload-to-cloud">
    <input type="hidden" name="fileUpload.meta">
    <input type="file" name="fileUpload" onchange="onFileUploadChange(event)">
</form>

示例FormData

function onSubmit(event) {
    event.preventDefault();

    var form = document.getElementById('my-upload-form');
    var formData = new FormData();

    var fileUpload = form.elements['fileUpload'];
    var fileUploadMeta = JSON.stringify({
        size: fileUpload.size,
        name: fileUpload.name,
        type: fileUpload.type,
        lastUpdated: fileUpload.lastUpdated
    });

    // Append fileUploadMeta BEFORE fileUpload.
    formData.append('fileUpload.meta', fileUploadMeta);
    formData.append('fileUpload', fileUpload);

    // Do whatever you do to POST here.
}
于 2017-06-14T00:24:33.427 回答