I have a form where users can upload PDFs, which I store in the database as BLOBs. I'm showing a list of all the PDFs uploaded, all of which can be downloaded by a click. I've tried so many different workarounds to get the PDF to download properly but it will say "Failed to load PDF document" in the browser and "The file is damaged and could not be repaired" in Adobe Acrobat. Here is my code:
Instructors.cfc (form for uploading file)
<form method="post" enctype="multipart/form-data">
<input id="document_filename" name="document_filename" type="hidden">
<input id="document_title" name="document_title" type="hidden">
<input id="openFileBrowser" type="button" value="Import Data from Application PDF" onclick="document.getElementById('application_document').click();">
<input id="application_document" name="application_document" type="file" accept=".pdf" style="display:none">
<input id="upload_document" type="button" onclick="UploadDocument()" style="width:220px; display: none" value="Upload Instructor Application Form">
</form>
<script>
function UploadDocument() {
var fd = new FormData();
var theFile = document.getElementById("application_document").files[0];
fd.append('uploadedFile', theFile);
fd.append('file_name', document.getElementById("document_title").value);
$.ajax({
url: "InstructorForms.cfc?method=getApplicationPDFData",
type: "post",
data: fd,
processData: false,
contentType: false,
cache: false
});
</script>
InstructorForms.cfc (inserts PDF blob to database)
<cffunction name="getApplicationPDFData" access="remote">
<cfset uploadDirectory = "#expandPath('../UPLOADS')#">
<cfif not directoryExists(uploadDirectory)>
<cfdirectory action="create" directory="#uploadDirectory#">
</cfif>
<cfif IsDefined("uploadedFile")>
<cffile action="upload" fileField="uploadedFile" destination="#uploadDirectory#" nameConflict="overwrite" accept="application/pdf">
</cfif>
<cfif IsDefined("file_name")>
<cfset filePath = uploadDirectory & "" & file_name>
<cfpdfform action="read" source="#filePath#" result="documentStruct" />
<cfset nameArray = documentStruct.Name.split(",")>
<cffile action="readbinary" file="#filePath#" variable="binPDF">
<cfquery name="addPDFToDB" datasource="#request.dsn#">
INSERT INTO DDMS.UPLOADED_FILES (LAST_NAME, FIRST_NAME, DOCUMENT, DOCUMENT_TYPE)
VALUES(<cfqueryparam value="#nameArray[1]#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#ltrim(rtrim(nameArray[2]))#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#binPDF#" cfsqltype="cf_sql_blob">,
'Instructor Application')
</cfquery>
<cffile action="delete" file="#filePath#">
</cffunction>
Instructors.cfc [again] (downloading PDF from database, where I'm having trouble)
<cffunction name="downloadPDF" access="remote" returntype="any">
<cfargument name="uploaded_file_id" required="yes" type="numeric">
<cfquery name="getInstructorApplication" datasource="#request.dsn#" result="output">
SELECT DOCUMENT, FIRST_NAME, LAST_NAME FROM DDMS.UPLOADED_FILES WHERE UPLOADED_FILE_ID = #arguments.uploaded_file_id#
</cfquery>
<cfset fileName = getInstructorApplication.LAST_NAME & "_" & getInstructorApplication.FIRST_NAME & "_application.pdf">
<cfset cfTags = "">
<cfsavecontent variable="cfTags">
<cfheader name="content-disposition" value="attachment; filename=#fileName#">
<cfcontent variable="#getInstructorApplication.DOCUMENT#" type="application/pdf" reset="yes">
</cfsavecontent>
<cfreturn cfTags>
</cffunction>
The most important section of code is the last/above snippet I included. Even when I navigate to the downloadPDF
function in the browser, it still won't download the PDF properly and gives the error messages. So cleaning that method up is step #1, and then I can actually retrieve the PDF on the user's page through an AJAX call, which I will also show in case it is helpful:
$(".pdfFile").on("click", function() {
var uploaded_file_id = $(this).data("id");
$.ajax({
url: "CFC/Instructors.cfc?method=downloadPDF",
data: { "uploaded_file_id": uploaded_file_id },
success: function(blob, status, xhr) {
var filename = "";
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=
]*=((['"]).*?2|[^;
]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var newBlob = new Blob([blob], {type: "application/pdf"});
var downloadUrl = URL.createObjectURL(newBlob);
if (filename) {
var a = document.createElement("a");
if (typeof a.download === 'undefined') {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location.href = downloadUrl;
}
}
}
});
});
Sorry for the overwhelming amount of code. But like I said, the most important part is in the downloadPDF
function, where I utilize cfcontent
and need to load the binary data properly. Any help would be greatly appreciated, as I've been stuck on this problem for a while and can't find much documentation.
UPDATE:
The PDF returned is of size 62.5 KB, and I've heard that output can be truncated to 64 KB due to a buffer in ColdFusion Admin if BLOB retrieval is disabled. I don't have access to ColdFusion Admin, yet one of my coworkers does, and perhaps he edited a wrong setting when attempting to enable BLOB retrieval globally. I'll check with him.
question from:
https://stackoverflow.com/questions/65854046/display-blob-file-from-database-in-coldfusion