简体   繁体   English

使用node.js在Azure文件存储中上载文件

[英]Uploading a file in Azure File Storage using node.js

We are trying create an webservice to upload files to Azure file storage using node.js service. 我们正在尝试使用node.js服务创建Web服务以将文件上载到Azure文件存储。

Below is the node.js server code. 下面是node.js服务器代码。

exports.post = function(request, response){
var shareName = request.headers.sharename;
var dirPath = request.headers.directorypath;
var fileName = request.headers.filename;

var body;
var length;

request.on("data", function(chunk){
    body += chunk;
    console.log("Get data");
});


request.on("end", function(){
    try{
        console.log("end");
        var data = body;
        length = data.length;

console.log(body); // This giving the result as undefined
console.log(length);

        fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) {
            if (!error) {
                // file uploaded
                response.send(statusCodes.OK, "File Uploaded");
            }else{
                response.send(statusCodes.OK, "Error!");
            }
        });

    }catch (er) {
response.statusCode = 400;
return res.end('error: ' + er.message);
}

});

}

Below is our client to upload a file. 以下是我们上传文件的客户端。

private static void sendPOST() throws IOException {
    URL obj = new URL("https://crowdtest-fileservice.azure-mobile.net/api/files_stage/");
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    con.setRequestMethod("POST");
    con.setRequestProperty("sharename", "newamactashare");
    con.setRequestProperty("directorypath", "MaheshApp/TestLibrary/");
    con.setRequestProperty("filename", "temp.txt");


    Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");
    byte[] data = Files.readAllBytes(path);

    // For POST only - START
    con.setDoOutput(true);
    OutputStream os = con.getOutputStream();
    os.write(data);
    os.flush();
    os.close();
    // For POST only - END

    int responseCode = con.getResponseCode();
    System.out.println("POST Response Code :: " + responseCode);

    if (responseCode == HttpURLConnection.HTTP_OK) { // success
        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
            System.out.println(inputLine);
        }
        in.close();

        // print result
        System.out.println(response.toString());
    } else {
        BufferedReader br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
        String line = "";
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        System.out.println("POST request not worked");
    }
}

It is showing the error 它显示错误

The request 'POST /api/files_stage/' has timed out. 请求'POST / api / files_stage /'已超时。 This could be caused by a script that fails to write to the response, or otherwise fails to return from an asynchronous call in a timely manner. 这可能是由于脚本无法写入响应,或者无法及时从异步调用返回而导致的。

Updated: 更新:

I have also tried below code. 我也试过下面的代码。

  var body = new Object();
  body = request.body;
  var length = body.length;

  console.log(request.body);
  console.log(body);
  console.log(length);

    try {
        fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) {
            if (!error) {
                // file uploaded
                response.send(statusCodes.OK, "File Uploaded");
            }else{
                response.send(statusCodes.OK, "Error!");
            }
        });
    } catch (ex) {
            response.send(500, { error: ex.message });
    }

But facing the issue 但面对这个问题

{"error":"Parameter stream for function createFileFromStream should be an object"} {“error”:“函数createFileFromStream的参数流应该是一个对象”}

I am new to node.js. 我是node.js的新手。 Please help me to fix this. 请帮我解决这个问题。

There are several issue here. 这里有几个问题。 Let us go over them one by one. 让我们逐一介绍它们。

1. In your Java client you cannot just dump the binary data into an Azure mobile service connection. 1.在Java客户端中,您不能只将二进制数据转储到Azure移动服务连接中。

The reason for this is that an Azure mobile service has two body parsers that ensure that no matter what, the request body is parsed for you. 原因是Azure移动服务有两个主体解析器,无论如何都能确保为您解析请求主体。 So, while you can walk around the Express body parser by specifying an uncommon content type, you will still hit the Azure body parser that will mess up your data stream by naively assuming that it is a UTF-8 string. 因此,虽然您可以通过指定不常见的内容类型来遍历Express正文解析器,但您仍然会通过天真地假设它是UTF-8字符串来点击Azure主体解析器,这会破坏您的数据流。

The only option therefore is to skip the Express parser by specifying a content type it cannot handle and then play along with the Azure parser by encoding your binary data with Base64 encoding. 因此,唯一的选择是通过指定无法处理的内容类型跳过Express解析器,然后通过使用Base64编码对二进制数据进行编码来与Azure解析器一起播放。

So, in the Java client replace 所以,在Java客户端替换

Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");
byte[] data = Files.readAllBytes(path);

with

con.setRequestProperty("content-type", "binary");    
Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");
byte[] data = Files.readAllBytes(path);
data = Base64.getEncoder().encode(data);

If you are not on Java 8, replace the java.util.Base64 encoder with any other Base64 encoder you have access to. 如果您不在Java 8上,请将java.util.Base64编码器替换为您有权访问的任何其他Base64编码器。

2. The createFileFromStream Azure storage api function you are trying to use expects a stream. 2.您尝试使用的createFileFromStream Azure存储api函数需要一个流。

At the same time, the best you can get when parsing a request body manually is a byte array. 同时,手动解析请求主体时可以获得的最佳结果是字节数组。 Unfortunately, Azure mobile services use NodeJS version 0.8, which means there is no easy way to construct a readable stream from a byte array, and you you will have to assemble your own stream suitable for Azure storage api. 不幸的是,Azure移动服务使用NodeJS版本0.8,这意味着没有简单的方法来构建来自字节数组的可读流,并且您必须组装适合Azure存储API的自己的流。 Some duct tape and stream@0.0.1 should do just fine. 一些胶带和stream@0.0.1应该没问题。

var base64 = require('base64-js'),
    Stream = require('stream'),
    fileService = require('azure-storage')
        .createFileService('yourStorageAccount', 'yourStoragePassword');

exports.post = function (req, res) {
    var data = base64.toByteArray(req.body),
        buffer = new Buffer(data),
        stream = new Stream();
        stream['_ended'] = false;
        stream['pause'] = function() {
            stream['_paused'] = true;
        };
        stream['resume'] = function() {
            if(stream['_paused'] && !stream['_ended']) {
                stream.emit('data', buffer);
                stream['_ended'] = true;
                stream.emit('end');
            }
        }; 
    try {
        fileService.createFileFromStream(req.headers.sharename, req.headers.directorypath, 
            req.headers.filename, stream, data.length, function (error, result, resp) {
                res.statusCode = error ? 500 : 200;
                res.end();
            }
        );
    } catch (e) {
        res.statusCode = 500;
        res.end();
    }
};

These are the dependencies you need for this sample. 这些是此示例所需的依赖项。

"dependencies": {   
    "azure-storage": "^0.7.0",
    "base64-js": "^0.0.8",
    "stream": "0.0.1"
}

If specifying them in your service's package.json does not work you can always go to this link and install them manually via the console. 如果在服务的package.json中指定它们不起作用,您可以随时转到此链接并通过控制台手动安装它们。

cd site\wwwroot
npm install azure-storage
npm install base64-js
npm install stream@0.0.1

3. To increase the default upload limit of 1Mb, specify MS_MaxRequestBodySizeKB for your service. 3.要增加1Mb的默认上载限制,请为您的服务指定MS_MaxRequestBodySizeKB。

MS_MaxRequestBodySizeKB

Do keep in mind though that since you are transferring you data as Base64-encoded you have to account for this overhead. 请记住,因为您将数据作为Base64编码传输,您必须考虑到这种开销。 So, to support uploading files up to 20Mb in size, you have to set MS_MaxRequestBodySizeKB to roughly 20 * 1024 * 4 / 3 = 27307. 因此,要支持上传大小为20Mb的文件,您必须将MS_MaxRequestBodySizeKB设置为大约20 * 1024 * MS_MaxRequestBodySizeKB = 27307。

When the request arrives at the function defined in exports.post , the whole request is already there, so you don't need to buffer it. 当请求到达exports.post定义的函数时,整个请求已经存在,因此您不需要缓冲它。 You can simplify it by writing something along the lines of the code below. 您可以通过编写下面代码行的内容来简化它。

exports.post = function(request, response){
    var shareName = request.headers.sharename;
    var dirPath = request.headers.directorypath;
    var fileName = request.headers.filename;

    var body = request.body;
    var length = body.length;

    console.log(length);

    try {
        fileService.createFileFromText(shareName, dirPath, fileName, body, function(error, result, resp) {
            if (!error) {
                // file uploaded
                response.send(statusCodes.OK, "File Uploaded");
            } else {
                response.send(statusCodes.OK, "Error!");
            }
        });
    } catch (ex) {
        response.send(500, { error: ex.message });
    }
}

I find the easiest way is to use pkgcloud which abstracts the differences between cloud providers and also provides a clean interface for uploading and downloading files. 我发现最简单的方法是使用pkgcloud来抽象云提供商之间的差异,并提供一个干净的界面来上传和下载文件。 It uses streams so the implementation is memory efficient as well. 它使用流,因此实现也是内存效率。

var pkgcloud = require('pkgcloud')
var fs = require('fs')
var client = pkgcloud.storage.createClient({
  provider: 'azure',
  storageAccount: 'your-storage-account',
  storageAccessKey: 'your-access-key'
});

var readStream = fs.createReadStream('a-file.txt');
var writeStream = client.upload({
  container: 'your-storage-container',
  remote: 'remote-file-name.txt'
});

writeStream.on('error', function (err) {
  // handle your error case
});

writeStream.on('success', function (file) {
  // success, file will be a File model
});

readStream.pipe(writeStream);

We can leverage this answer of the thread on SO How to send an image from Android client to Node.js server via HttpUrlConnection? 我们可以在SO上利用这个线程的答案如何通过HttpUrlConnection将图像从Android客户端发送到Node.js服务器? , which create a custom middleware to get the upload file content into a buffer array, then we can use createFileFromText() to store the file in Azure Storage. ,创建自定义中间件以将上传文件内容放入缓冲区数组,然后我们可以使用createFileFromText()将文件存储在Azure存储中。

Here is the code snippet: 这是代码片段:

function rawBody(req, res, next) {
    var chunks = [];

    req.on('data', function (chunk) {
        chunks.push(chunk);
    });

    req.on('end', function () {
        var buffer = Buffer.concat(chunks);

        req.bodyLength = buffer.length;
        req.rawBody = buffer;
        next();
    });

    req.on('error', function (err) {
        console.log(err);
        res.status(500);
    });
}
router.post('/upload', rawBody,function (req, res){

    fileService.createShareIfNotExists('taskshare', function (error, result, response) {
        if (!error) {
            // if result = true, share was created.
            // if result = false, share already existed.
            fileService.createDirectoryIfNotExists('taskshare', 'taskdirectory', function (error, result, response) {
                if (!error) {
                    // if result = true, share was created.
                    // if result = false, share already existed.
                    try {
                        fileService.createFileFromText('taskshare', 'taskdirectory', 'test.txt', req.rawBody, function (error, result, resp) {
                            if (!error) {
                                // file uploaded
                                res.send(200, "File Uploaded");
                            } else {
                                res.send(200, "Error!");
                            }
                        });
                    } catch (ex) {
                        res.send(500, { error: ex.message });
                    }

                }
            });
        }
    });

})
router.get('/getfile', function (req, res){
    fileService.createReadStream('taskshare', 'taskdirectory', 'test.txt').pipe(res);
})

There are several things: 有几件事:

1. createFileFromText can work with plain text. 1. createFileFromText可以使用纯文本。 But it will fail for those binary content, as it uses UTF-8 encoding. 但是对于那些二进制内容来说它会失败,因为它使用UTF-8编码。

You might want to refer to the similar issue for blob at: Saving blob (might be data!) returned by AJAX call to Azure Blob Storage creates corrupt image 您可能想要在blob中引用类似的问题: 通过AJAX调用Azure Blob存储返回的保存blob(可能是数据!)会产生损坏的图像

2. The createFileFromStream or createWriteStreamToExistingFile \\ createWriteStreamToNewFile Azure storage API may be the function can help. 2. createFileFromStreamcreateWriteStreamToExistingFile \\ createWriteStreamToNewFile Azure存储API可能对该功能有所帮助。

Please be noted that these APIs are target to streams. 请注意,这些API是流的目标。 You need convert your buffer/string in the request body to a stream. 您需要将请求正文中的缓冲区/字符串转换为流。 You can refer to How to wrap a buffer as a stream2 Readable stream? 您可以参考如何将缓冲区包装为stream2可读流?

For createFileFromStream : 对于createFileFromStream

fileService.createFileFromStream(req.headers.sharename, 
  req.headers.directorypath, 
  req.headers.filename, 
  requestStream, 
  data.length, 
  function (error, result, resp) {
    res.statusCode = error ? 500 : 200;
    res.end();
  }
);

For createWriteStreamToNewFile : 对于createWriteStreamToNewFile

var writeStream = fileService.createWriteStreamToNewFile(req.headers.sharename, 
  req.headers.directorypath, 
  req.headers.filename, 
  data.length);

requestStream.pipe(writeStream);

3. There are several issues in your code 3.您的代码中存在几个问题

console.log(body); // This giving the result as undefined

The reason is you define var body and it is undefined . 原因是你定义了var body并且它是undefined The code body += chunk will still make body undefined. 代码body += chunk仍然会使body未定义。

fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) {
  if (!error) {
    // file uploaded
    response.send(statusCodes.OK, "File Uploaded");
  }else{
    response.send(statusCodes.OK, "Error!");
  }
});

When error happens in createFileFromStream , it could also be an error in the network transfer, you might also want to return the error code instead of statusCodes.OK . createFileFromStream发生错误时,它也可能是网络传输中的错误,您可能还希望返回错误代码而不是statusCodes.OK

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM