java - 如何使用 servlet 从 oracle 数据库下载 pdf 文件而不损坏文件?
问题描述
我有一个 servlet,我在其中根据 id 从 oracle 数据库中检索 pdf 文件并将其写入响应流。但是当我尝试这样做时,下载的文件已损坏并且文件大小为零。Adobe 阅读器给出错误提示“Adobe 无法打开“myfile.pdf”,因为它不是受支持的类型...”。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@MultipartConfig( fileSizeThreshold = 1024 * 1024,
maxFileSize = 1024 * 1024 * 5, maxRequestSize = 1024 * 1024 * 5 * 5)
public class DBFileDownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// size of byte buffer to send file
private static final int BUFFER_SIZE = 4096;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// get file id from URL's parameters
String course_code = request.getParameter("course_code");
Connection conn = null; // connection to the database
try {
// connects to the database
Connection con = JDBCfile.getOracleConnection();
// queries the database
String sql = "SELECT * FROM course_syllabus WHERE course_code = ?";
PreparedStatement statement = con.prepareStatement(sql);
statement.setString(1, course_code);
ResultSet result = statement.executeQuery();
if (result.next()) {
// gets file name and file blob data
String fileName = result.getString("file_name");
Blob blob = result.getBlob("syllabus_file");
InputStream inputStream = blob.getBinaryStream();
int fileLength = inputStream.available();
System.out.println("fileLength = " + fileLength);
ServletContext context = getServletContext();
// sets MIME type for the file download
String mimeType = context.getMimeType(fileName);
if (mimeType == null) {
mimeType = "application/octet-stream";
}
// set content properties and header attributes for the response
response.setContentType(mimeType);
response.setContentLength(fileLength);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", fileName);
response.setHeader(headerKey, headerValue);
// writes the file to the client
OutputStream outStream = response.getOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outStream.close();
} else {
// no file found
response.getWriter().print("File not found for the file id: " + course_code);
}
}catch (SQLException ex) {
ex.printStackTrace();
response.getWriter().print("SQL Error: " + ex.getMessage());
} catch (IOException ex) {
ex.printStackTrace();
response.getWriter().print("IO Error: " + ex.getMessage());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCfile.cleanup(con,statement); //closes connection
}
}
}
course_syllabus 表有列: course_code varchar,file_name varchar,syllabus_file (blob)
一切都完美执行,但我下载的文件有零字节,没有什么可读取的。我是 servlet 编程的新手,有人知道吗?请也发布一个有效的解决方案。提前致谢。
解决方案
这里有一个问题:
int fileLength = inputStream.available();
available() 不返回流中的总字节数。它只返回可以在不阻塞的情况下读取多少字节。从文档中:
请注意,虽然 的某些实现
InputStream
会返回流中的字节总数,但许多不会。使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是不正确的。
使用Blob.length()代替:
long fileLength = blob.length();
// ...
response.setContentLengthLong(fileLength);
另一个问题是您的错误处理。如果方法没有成功,请不要表现得像方法成功一样。如果您的方法无法成功获取文件,您希望HTTP 调用返回错误。
首先,删除catch (IOException ex)
块。如果存在 IOException,您希望它传播,因此 HTTP 调用将失败。
其他两个块需要传播它们的错误:
} catch (SQLException ex) {
throw new ServletException(ex);
} catch (ClassNotFoundException e) {
throw new ServletException(e);
同样,您要做的不仅仅是在 ResultSet 为空时打印“找不到文件”。有一个 HTTP 响应代码专门用于指示 HTTP 请求因未找到请求的资源而失败:
if (result.next()) {
// ...
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.getWriter().print("File not found for the file id: " + course_code);
}
推荐阅读
- azure - 将“使用 Apple 登录”与 Azure AD B2C 集成时,我们可以跳过电子邮件屏幕吗?
- google-sheets-importxml - 使用 IMPORTXML 时,为什么我可以得到 IMG ALT 而不是 IMG SRC?
- r - Pivot_longer() 在 dplyr 中的多组列上
- reactjs - 来自 api 的图像在反应中无法渲染
- javascript - 鼠标单击图像时更改元素
- java - 在 libglib 中捕获 int3 错误 - debian eclipse
- mysql - 平均售价中的Mysql排序语句
- python - 如何始终将所有导入添加到新的 Python 文件?
- python - 单个图表中的堆积条形图和条形图
- ios - ios 使用 ngx-google-places-autocomplete 时没有调用返回