使用 node.js 在 Azure 文件存储中上传文件
Uploading a file in Azure File Storage using node.js
我们正在尝试创建一个 Web 服务,以使用 node.js 服务将文件上传到 Azure 文件存储。
下面是 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);
}
});
}
下面是我们客户端上传文件。
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");
}
}
显示错误
The request 'POST /api/files_stage/' has timed out. 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.
更新:
我也试过下面的代码。
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 });
}
但面临问题
{"error":"Parameter stream for function createFileFromStream should be
an object"}
我是 node.js 的新手。请帮我解决这个问题。
当请求到达exports.post
中定义的函数时,整个请求已经存在,所以不需要缓冲。您可以按照以下代码编写一些内容来简化它。
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 });
}
}
我发现最简单的方法是使用 pkgcloud,它抽象了云提供商之间的差异,还为上传和下载文件提供了一个干净的界面。它使用流,因此实现也具有内存效率。
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);
我们可以利用 SO 上线程的这个答案,它创建一个自定义中间件来将上传文件内容放入缓冲区数组,然后我们可以使用 createFileFromText()
来存储文件在 Azure 存储中。
这是代码片段:
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);
})
这里有几个问题。让我们一一回顾。
1.在您的 Java 客户端中,您不能只将二进制数据转储到 Azure 移动服务连接中。
这是因为 Azure 移动服务有两个正文解析器,可确保无论如何都会为您解析请求正文。
因此,虽然您可以通过指定不常见的内容类型来绕过 Express 主体解析器,但您仍然会遇到 Azure 主体解析器,它会天真地假设它是 UTF-8 字符串,从而弄乱您的数据流。
因此,唯一的选择是通过指定它无法处理的内容类型来跳过 Express 解析器,然后通过使用 Base64 编码对二进制数据进行编码来与 Azure 解析器一起使用。
所以,在Java客户端替换
Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");
byte[] data = Files.readAllBytes(path);
和
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);
如果您不在 Java 8 上,请将 java.util.Base64 编码器替换为您有权访问的任何其他 Base64 编码器。
2。您尝试使用的 createFileFromStream
Azure 存储 api 函数需要流。
同时,手动解析请求体时,你能得到的最好的是一个字节数组。不幸的是,Azure 移动服务使用 NodeJS 版本 0.8,这意味着没有简单的方法从字节数组构建可读流,您将不得不 assemble 适合 Azure 存储的自己的流 api .一些胶带和 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();
}
};
这些是此示例所需的依赖项。
"dependencies": {
"azure-storage": "^0.7.0",
"base64-js": "^0.0.8",
"stream": "0.0.1"
}
如果在您的服务 package.json 中指定它们不起作用,您可以随时转到此 link 并通过控制台手动安装它们。
cd site\wwwroot
npm install azure-storage
npm install base64-js
npm install stream@0.0.1
3。要增加 1Mb 的默认上传限制,请为您的服务指定 MS_MaxRequestBodySizeKB。
请记住,由于您将数据传输为 Base64 编码,因此您必须考虑此开销。因此,要支持上传最大 20Mb 的文件,您必须将 MS_MaxRequestBodySizeKB
设置为大约 20 * 1024 * 4 / 3 = 27307。
有几件事:
1. createFileFromText
可以处理纯文本。但是对于那些二进制内容它会失败,因为它使用 UTF-8 编码。
您可能想在以下位置参考 blob 的类似问题:Saving blob (might be data!) returned by AJAX call to Azure Blob Storage creates corrupt image
2。 createFileFromStream
或 createWriteStreamToExistingFile
\ createWriteStreamToNewFile
Azure 存储 API 可能是可以提供帮助的功能。
请注意,这些 API 是针对流的。您需要将请求正文中的 buffer/string 转换为流。可以参考How to wrap a buffer as a stream2 Readable stream?
对于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();
}
);
对于createWriteStreamToNewFile
:
var writeStream = fileService.createWriteStreamToNewFile(req.headers.sharename,
req.headers.directorypath,
req.headers.filename,
data.length);
requestStream.pipe(writeStream);
3。您的代码中存在多个问题
console.log(body); // This giving the result as undefined
原因是你定义了var 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!");
}
});
当createFileFromStream
发生错误时,也可能是网络传输错误,您可能还想return错误代码而不是statusCodes.OK
。
我们正在尝试创建一个 Web 服务,以使用 node.js 服务将文件上传到 Azure 文件存储。
下面是 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);
}
});
}
下面是我们客户端上传文件。
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");
}
}
显示错误
The request 'POST /api/files_stage/' has timed out. 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.
更新:
我也试过下面的代码。
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 });
}
但面临问题
{"error":"Parameter stream for function createFileFromStream should be an object"}
我是 node.js 的新手。请帮我解决这个问题。
当请求到达exports.post
中定义的函数时,整个请求已经存在,所以不需要缓冲。您可以按照以下代码编写一些内容来简化它。
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 });
}
}
我发现最简单的方法是使用 pkgcloud,它抽象了云提供商之间的差异,还为上传和下载文件提供了一个干净的界面。它使用流,因此实现也具有内存效率。
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);
我们可以利用 SO createFileFromText()
来存储文件在 Azure 存储中。
这是代码片段:
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);
})
这里有几个问题。让我们一一回顾。
1.在您的 Java 客户端中,您不能只将二进制数据转储到 Azure 移动服务连接中。
这是因为 Azure 移动服务有两个正文解析器,可确保无论如何都会为您解析请求正文。 因此,虽然您可以通过指定不常见的内容类型来绕过 Express 主体解析器,但您仍然会遇到 Azure 主体解析器,它会天真地假设它是 UTF-8 字符串,从而弄乱您的数据流。
因此,唯一的选择是通过指定它无法处理的内容类型来跳过 Express 解析器,然后通过使用 Base64 编码对二进制数据进行编码来与 Azure 解析器一起使用。
所以,在Java客户端替换
Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");
byte[] data = Files.readAllBytes(path);
和
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);
如果您不在 Java 8 上,请将 java.util.Base64 编码器替换为您有权访问的任何其他 Base64 编码器。
2。您尝试使用的 createFileFromStream
Azure 存储 api 函数需要流。
同时,手动解析请求体时,你能得到的最好的是一个字节数组。不幸的是,Azure 移动服务使用 NodeJS 版本 0.8,这意味着没有简单的方法从字节数组构建可读流,您将不得不 assemble 适合 Azure 存储的自己的流 api .一些胶带和 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();
}
};
这些是此示例所需的依赖项。
"dependencies": {
"azure-storage": "^0.7.0",
"base64-js": "^0.0.8",
"stream": "0.0.1"
}
如果在您的服务 package.json 中指定它们不起作用,您可以随时转到此 link 并通过控制台手动安装它们。
cd site\wwwroot
npm install azure-storage
npm install base64-js
npm install stream@0.0.1
3。要增加 1Mb 的默认上传限制,请为您的服务指定 MS_MaxRequestBodySizeKB。
请记住,由于您将数据传输为 Base64 编码,因此您必须考虑此开销。因此,要支持上传最大 20Mb 的文件,您必须将 MS_MaxRequestBodySizeKB
设置为大约 20 * 1024 * 4 / 3 = 27307。
有几件事:
1. createFileFromText
可以处理纯文本。但是对于那些二进制内容它会失败,因为它使用 UTF-8 编码。
您可能想在以下位置参考 blob 的类似问题:Saving blob (might be data!) returned by AJAX call to Azure Blob Storage creates corrupt image
2。 createFileFromStream
或 createWriteStreamToExistingFile
\ createWriteStreamToNewFile
Azure 存储 API 可能是可以提供帮助的功能。
请注意,这些 API 是针对流的。您需要将请求正文中的 buffer/string 转换为流。可以参考How to wrap a buffer as a stream2 Readable stream?
对于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();
}
);
对于createWriteStreamToNewFile
:
var writeStream = fileService.createWriteStreamToNewFile(req.headers.sharename,
req.headers.directorypath,
req.headers.filename,
data.length);
requestStream.pipe(writeStream);
3。您的代码中存在多个问题
console.log(body); // This giving the result as undefined
原因是你定义了var 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!");
}
});
当createFileFromStream
发生错误时,也可能是网络传输错误,您可能还想return错误代码而不是statusCodes.OK
。