17

Stiamo provando a creare un servizio Web per caricare i file nell'archivio file di Azure utilizzando il servizio node.js.Caricamento di un file in Archiviazione file di Azure utilizzando node.js

Di seguito è riportato il codice del server 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); 
} 

}); 

} 

Di seguito è il nostro client per caricare un 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"); 
    } 
} 

Sta mostrando l'errore

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.

Aggiornato:

Ho anche provato sotto il codice.

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 }); 
    } 

Ma di fronte al problema

{"error":"Parameter stream for function createFileFromStream should be an object"}

Sono nuovo di node.js. Per favore aiutami a risolvere questo problema.

risposta

7

ci sono diverse problema qui. Andiamo su di loro uno per uno.

1. Nel client Java non è possibile scaricare i dati binari in una connessione del servizio mobile di Azure.

Il motivo per questo è che un servizio mobile di Azure ha due parser del corpo che assicurano che, indipendentemente da cosa, il corpo della richiesta venga analizzato per voi. Quindi, mentre è possibile aggirare il parser del corpo Express specificando un tipo di contenuto non comune, si continuerà a premere il parser del corpo di Azure che rovinerà il flusso di dati assumendo ingenuamente che si tratti di una stringa UTF-8.

L'unica opzione è quindi saltare il parser Express specificando un tipo di contenuto che non può gestire e quindi giocare insieme al parser di Azure codificando i dati binari con la codifica Base64.

Così, nel client Java sostituire

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

con

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); 

Se non siete su Java 8, sostituire il codificatore java.util.Base64 con qualsiasi altro encoder Base64 si ha accesso a.

2. La funzione di memoria di archiviazione di Azure che si sta tentando di utilizzare si aspetta un flusso.

Allo stesso tempo, il meglio che è possibile ottenere quando si analizza manualmente un corpo di richiesta è un array di byte. Sfortunatamente, i servizi mobili di Azure utilizzano NodeJS versione 0.8, il che significa che non esiste un modo semplice per costruire un flusso leggibile da un array di byte, e dovrai assemblare il tuo stream adatto per l'API di archiviazione di Azure. Qualche nastro adesivo e [email protected] dovrebbero andare bene.

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(); 
    } 
}; 

Queste sono le dipendenze necessarie per questo esempio.

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

Se li specificando nella package.json del servizio non funziona si può sempre andare a questo link e installarli manualmente tramite la console.

cd site\wwwroot 
npm install azure-storage 
npm install base64-js 
npm install [email protected]0.0.1 

3. Per aumentare il limite di upload di default di 1Mb, specificare MS_MaxRequestBodySizeKB per il vostro servizio.

MS_MaxRequestBodySizeKB

Non tenere a mente però che dal momento che si stanno trasferendo i dati come codifica Base64 si deve tenere conto di questo overhead. Così, per supportare il caricamento di file fino a 20 MB di dimensione, è necessario impostare MS_MaxRequestBodySizeKB a circa 20 * 1024 * 4/3 = 27307.

+0

Grazie a @Roman Pletnev. Funziona bene, ma se il file è più di un MB lancia un errore "{" codice ": 413," errore ":" Errore: il limite di dimensione massima del corpo della richiesta era superato. »Per impostazione predefinita,' ContentMD5' è disabilitato. C'è qualche altra opzione per caricare file più grandi (sotto 20 MB). – Sniper

3

Quando la richiesta arriva alla funzione definita in exports.post, l'intera richiesta è già presente, quindi non è necessario memorizzarla. Puoi semplificarlo scrivendo qualcosa seguendo le linee del codice qui sotto.

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 }); 
    } 
} 
+0

Grazie #carlosfigueira. Ho provato questo ma sta dando un nuovo errore. "{" errore ":" Il flusso di parametri per la funzione createFileFromStream dovrebbe essere un oggetto "}". Ho provato a dichiarare il "corpo" come un oggetto ma mostrando ancora lo stesso problema. – Sniper

+0

Dovresti essere in grado di usare 'createFileFromText', dato che hai già tutto il contenuto del file con te. – carlosfigueira

+0

A proposito, ho aggiornato la risposta per utilizzare quella funzione. – carlosfigueira

3

trovo il modo più semplice è quello di utilizzare pkgcloud che astrae le differenze tra fornitori di cloud e fornisce anche un'interfaccia pulita per caricare e scaricare file. Usa flussi in modo tale che anche l'implementazione sia efficiente in termini di memoria.

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); 
2

Possiamo sfruttare questa risposta del thread su SO How to send an image from Android client to Node.js server via HttpUrlConnection?, che creano un middleware personalizzato per ottenere il contenuto del file di upload in una matrice tampone, allora possiamo usare createFileFromText() per memorizzare il file in Azure.

Ecco il frammento di codice:

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); 
}) 
+0

Grazie #Gray. Ho provato questo, ma ancora mostrando lo stesso problema. Il controllo ora sta entrando nel "req.on ('data', function (chunk) {". Per favore aiutatemi su questo – Sniper

+0

Salve, qual è lo stesso problema a cui vi siete riferiti? Il problema "scaduto" o l'altro uno nella sezione di aggiornamento? –

+0

Il problema di timeout. – Sniper

0

Ci sono diverse cose:

1. createFileFromText può lavorare con plain testo. Ma fallirà per quei contenuti binari, poiché usa la codifica UTF-8.

Si potrebbe desiderare di fare riferimento al problema simile per il blob in: Saving blob (might be data!) returned by AJAX call to Azure Blob Storage creates corrupt image

2. Il createFileFromStream o createWriteStreamToExistingFile \ createWriteStreamToNewFile storage API Azure può essere la funzione può aiutare.

Si noti che queste API sono destinate ai flussi. È necessario convertire il buffer/stringa nel corpo della richiesta in un flusso. È possibile fare riferimento a How to wrap a buffer as a stream2 Readable stream?

Per 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(); 
    } 
); 

Per createWriteStreamToNewFile:

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

requestStream.pipe(writeStream); 

3.Ci sono diversi problemi nel codice

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

Il motivo è di definire var body ed è undefined. Il codice body += chunk continuerà a rendere non definito 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!"); 
    } 
}); 

Quando errore si verifica in createFileFromStream, potrebbe anche essere un errore nel trasferimento di rete, si potrebbe anche voler restituire il codice di errore invece di statusCodes.OK.