使用Windows Azure、Blob存储和Node.js上传并下载大文件

3
我正在使用Windows Azure做一个网站,允许用户通过网页表单提交文件,并使用POST方法发送表单。我正在使用Mac OS的最新版本,在一台2013年的Mac Book Air上。
服务器接收这个表单后,将文件流传到Blob中,并将用户重定向到一个新的带有Blob链接(公共的)的网页。这样,用户可以稍后获取他的文件。
对于小文件(大约35KB的图像,950MB的dmg文件),我的做法似乎很好用。
但是当涉及到更大的文件(大约20MB左右)时,服务器会在上传过程中出现错误(在大约1分20秒或2分40秒后,记不太清了)。客户端(Google Chrome)似乎并不知道这一点,并继续上传文件。但是当上传结束时,它会显示一个503错误。我尝试使用两个大小约为20MB的dmg文件,并得到了相同的结果(问题1)。
然后,对于一个大约57MB的DMG文件(Google Chrome安装程序)和几个大约700MB的文件(使用大约30MB/s的上传互联网连接),服务器似乎没有任何问题,客户端也没有问题。上传过程一直持续到结束,然后我得到了存储在Blob存储中的文件链接。但是当我下载它时,文件比预期的略大(对于一个57MB的文件增加了4096字节,对于一个约700MB的文件增加了大约40KB)。我尝试使用几种类型的文件:虽然有一些奇怪的东西,但足以能够回放视频,但是当我尝试挂载dmg时失败了(我猜是因为完整性检查做得不错)(问题2)。
我想知道哪些数据被添加到我的文件中。所以我创建了两个与我的大约57MB dmg文件相同大小的文件。第一个文件充满了字符0,第二个交替使用字符0和1。当我上传并下载它们时,它们的大小都不是原始大小,但它们仍然只包含000000....或010101010....。虽然真的有更多的0000...和更多的0101010....比原来的版本(但只有0和01)。
所以我去了Blob存储管理器页面,发现每个文件的大小似乎比在我的电脑上小(例如:54.33MB而不是大约57MB)。可能是因为不同的文件系统,...?
总结问题2:我上传一个大文件,在线上看起来比在我的笔记本电脑上小,然后我下载它,实际上比原来的版本要大。很奇怪,不是吗?有什么想法吗?谢谢您的帮助!
这是我的server.js文件:
var http = require('http');
var url = require("url");
var express = require('express');
var querystring = require('querystring');
var fs = require('fs');
var path = require('path');
var azure = require('azure');
var multiparty = require('multiparty');
var tools = require('./tools.js');

var app = express();
app.use(express.logger("dev"));

var port = process.env.PORT || 1337;
var containerName = 'publicblobs';

app.use('/public', express.static('./public'));

/*
 * \brief Displays upload form
 */
app.get('(/|/upload/?)', function(req, res) {
    res.redirect('/public/upload.html');
});

/*
 * \brief Handles upload post request.
 */
app.post('/upload/request/?', function (req, res) {
    var blobService = azure.createBlobService();
    var form = new multiparty.Form();
    var currentFileName = 'undefinedCurrentFileName';

    blobService.createContainerIfNotExists(containerName
    , {publicAccessLevel : 'blob'}
    , function(error){
        if(!error){
            // Container exists and is public
        } else {
            res.render('log.ejs', {log: error.message});
        }
    });

    function gotPart(part) {
        if (part.filename) {
            console.log("New file received !");
            currentFileName = part.filename;
            var size = part.byteCount;
            console.log("file size : " + size);

            var onError = function(error) {
                if (error) {
                    console.log("blobService.createBlockBlobFromStream : on error");
                    res.render('log.ejs', {log: error.message});
                } else {
                    console.log("onError() : No error");
                }
            };
            blobService.createBlockBlobFromStream(containerName, currentFileName, part, size, onError);
        } else {
            console.log("New part received (not a file)");
            form.handlePart(part);
        }
    }
    form.on('part', gotPart);


    function reqEnd() {
        console.log("Upload completed!");
        res.redirect('/upload/complete/?'+
            // 'url='+req.get('host')+'/library/download/'+targetFileName+//'+req.get('host')+'
            '&fileName='+currentFileName+
            '&result=success');
    }
    req.on('end', reqEnd);

    function handleErrorCallback(error) { // inspired from https://dev59.com/f2Ml5IYBdhLWcg3we283
        var errorLog = "There was an error while receiving the request. The client may have put an end to the connection : " + error;
        console.log(errorLog);
    }

    req.on('error', handleErrorCallback);

    form.on('error', handleErrorCallback);

    form.parse(req);
});

/*
 * \brief Returns the file to the user.
 */
app.get('/library/download/:fileName', function(req, res) {
    fs.readFile('./public/uploaded/'+req.params.fileName, function(error,data){
        res.end(data);
    });
});

/*
 * \brief Inform the user about his uploaded file (and about error if need be)
 */
app.get('/upload/complete/?', function(req, res) {
    var params = querystring.parse(url.parse(req.url).query);
    res.setHeader('Content-Type', 'text/html');
    res.charset = 'utf8';
    if (params['result'] === 'success') {
        res.render('uploadComplete.ejs', {log: 'File '+params['fileName']+' uploaded to ', url: url.parse('http://'+process.env['AZURE_STORAGE_ACCOUNT']+'.blob.core.windows.net/'+containerName+'/'+params['fileName']).href});
    } else {
        res.render('uploadComplete.ejs', {log: 'An error occurred while uploading '+params['fileName']+' : '+params['log'], url: ''});
    }

});

/*
 * \brief Handles wrong addresses
 */
app.use(function(req, res, next){
    res.setHeader('Content-Type', 'text/plain');
    res.charset = 'utf8';
    console.log("Redirection due to the fact that the page has not been found.");
    res.redirect('/upload');
});

app.listen(port);

这是我使用Bootstrap和jQuery创建的表单:

<div class="container">

  <div class="starter-template">
    <form class="form-signin" role="form" action="/upload/request" method="post" enctype="multipart/form-data">
        <h2 class="form-signin-heading">Select a file to upload</h2>
        <label>
          <input type="file" name="filename2" id="filename2" class="form-control" required>
      </label>
      <button class="btn btn-lg btn-primary btn-block" type="submit">Upload</button>
    </form>
</div>

这是我的web.config文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <webSocket enabled="false" />
    <handlers>
      <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
    </handlers>
    <rewrite>
      <rules>
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^server.js\/debug[\/]?" />
        </rule>
        <rule name="StaticContent">
          <action type="Rewrite" url="public{REQUEST_URI}"/>
        </rule>
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
          </conditions>
          <action type="Rewrite" url="server.js"/>
        </rule>
      </rules>
    </rewrite>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824"/>
      </requestFiltering>
    </security>
  </system.webServer>
  <system.web>
    <httpRuntime maxRequestLength="2097152"/>
  </system.web>
</configuration>

嘿,我在去年十月份遇到了类似的问题,只能使用标准上传(先写入服务器)。但现在Azure允许CORS,所以我正在尝试生成SAS URL并直接上传到Azure,无需使用多方或第三方上传。我会告诉你进展如何。 - Matt Kim
嗨,马特,感谢您的评论。我也在尝试CORS,目前为止,这是我得出的结果:http://stackoverflow.com/questions/22286856/windows-azure-set-blob-cors-properties-using-nodejs - user3380016
看看我的回答,相似问题 - Agni
1个回答

0

微软一直以来都习惯使用二的幂次方单位来描述文件大小,这可能会引起一些混淆。在微软术语中,“54.33 MB”=(54.33*1024*1024)字节,大约是57,000,000字节。因此,通过微软工具查看文件时,您所描述的行为是“按设计”,尽管我理解如果有点困惑。

现在,也许更正确的说法是54.33 Mebibytes(MiB)约为57 MB,但当微软使用MB = 1024 * 1024开始(1998年之前),Mebibyte术语还不存在。这只是您需要接受使用微软系统的一些事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接