2017-05-24 1 views
2

J'ai un simple (par exemple) script pour télécharger un fichier dans la base de données (Oracle, si elle importe):Passer un grand BLOB à une procédure stockée

<cfscript> 
param string filename; 

if (FileExists(filename)) 
{ 
    result = new StoredProc(
    datasource = "ds", 
    procedure = "FILE_UPLOAD", 
    result  = "NA", 
    parameters = [ 
     { value = FileReadBinary(filename), type = "in", cfsqltype = "CF_SQL_BLOB" } 
    ] 
).execute(); 
} 
</cfscript> 

Cependant, les ColdFusion CFML Reference états pour FileReadBinary(filepath):

Remarque: Cette action lit le fichier dans une variable de la portée Variables locale. Il n'est pas destiné à être utilisé avec des fichiers volumineux, tels que des journaux, car ils peuvent entraîner la fermeture du serveur.

Si je ne devais pas utiliser FileReadBinary(filepath), comment télécharger un fichier volumineux (0.5 - 1Tb)?

+1

De toute façon, vous ne devriez pas charger un fichier de cette taille via une interface Web. La requête expirera longtemps avant d'être téléchargée. Peu importe combien de temps vous remplacez le délai d'attente de la demande, cela semble avoir besoin d'une solution différente comme le téléchargement du fichier sur un serveur FTP, en dehors de CF et d'un autre processus chargé dans Oracle. –

+1

Je suis d'accord que ce n'est pas un usage typique, mais ne va sûrement pas échouer. Je développe une application produisant des centaines de réponses Mbytes pour les requêtes web, et ça marche bien. Bien sûr, les clients et le serveur sont dans le même LAN, mais je suppose que c'est pareil dans ce cas. – Galcoholic

Répondre

1

Comme suggéré par @Galcoholic, vous pouvez utiliser les classes Java sous-jacentes et CallableStatement.setBlob(int, InputStream):

<cfscript> 
param string filename; 

// Get the necessary Java classes: 
Files = createObject('java', 'java.nio.file.Files'); 
Paths = createObject('java', 'java.nio.file.Paths'); 

// Do not timeout the request 
setting requesttimeout = 0; 

try { 
    input = Files.newInputStream(Paths.get(filename, []), []); 

    connection = createObject('java', 'coldfusion.server.ServiceFactory') 
        .getDataSourceService() 
        .getDataSource('ds') 
        .getConnection() 
        .getPhysicalConnection(); 
    statement = connection.prepareCall('{call FILE_UPLOAD(?)}'); 
    statement.setBlob(JavaCast('int', 1), input); 
    statement.executeUpdate() 
} 
finally 
{ 
    if (isDefined("statement")) 
    statement.close(); 
    if (isDefined("connection")) 
    connection.close(); 
} 
</cfscript> 

Note:

  • Chaque argument doit être fourni pour une méthode Java; Par conséquent, pour les méthodes avec un nombre d'arguments variable, les arguments VARARGS doivent être passés en tant que tableau (ou tableau vide sans arguments supplémentaires).
  • Les valeurs numériques ColdFusion ne seront pas implicitement forcées dans les littéraux numériques Java. Par conséquent, JavaCast('int', value) est requis.
  • La gestion des erreurs n'est pas incluse dans l'exemple ci-dessus.
  • Si les fichiers ont été téléchargés, les paramètres "Taille maximale des données postales" et "Demander la mémoire du papillon" dans la console d'administration doivent être augmentés des tailles par défaut à une limite appropriée pour la taille des fichiers téléchargés (Sinon, coldfusion.util.MemorySemaphore va lancer des exceptions de mémoire insuffisante lorsqu'il gère le téléchargement avant que le script ne soit analysé).
1

Si l'utilisation de Java est une option, vous pouvez passer un objet InputStream à PreparedStatement pour remplir un champ Blob. Quelque chose comme ça, la gestion des exceptions et toutes les autres choses à ajouter:

Connection con = someDataSource.getConnection(); 
String sql = "INSERT INTO MY_TABLE(MY_BLOB) VALUES(?)"; 
PreparedStatement ps = con.prepareStatement(sql); 
InputStream fis = new FileInputStream("MyBigFile.big"); 
ps.setBlob(1, fis); 
ps.executeUpdate(); 

Je pense que Java va le faire en utilisant des tampons, et pas charger le fichier en mémoire.