我正在使用 pdf 查看器,它将从服务器(nodejs)加载 pdf 文件,然后在客户端呈现以允许用户直接在我的网站中阅读。
我正在使用 pdf.js 在客户端渲染 pdf 文件。问题是客户端必须下载整个 pdf 文件才能解析和渲染它,因此如果文件太大(在我的情况下约为 200MB),用户必须等待下载整个 200MB。
我研究了一下,我认为我可以通过两种方式解决这个问题:
在服务器端将大型 pdf 文件拆分为许多较小的 pdf 文件,并仅按需提供特定的小文件。但这样,我会丢失一些重要的元数据,例如 pdf 轮廓,...
直接在服务器端使用pdf.js,获取pdf页面,然后将每个页面作为二进制文件提供给客户端,客户端还将使用pdf.js(addPage函数)将每个页面添加到其查看器。但不知道可不可以。
那么我应该怎么做才能解决这个问题呢? 非常感谢。
我也面临着同样的问题。对于 2024.09,您可以尝试范围请求并加载 pdf 部分内容,而无需下载整个 pdf。您可以编写部分 api 并实现范围请求或配置静态服务器以支持部分请求。更多信息:https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#range
这是我正在使用的 api 代码的 Rust 版本(你可以使用任何语言来实现相同的逻辑):
pub fn get_partial_pdf(lastest_pdf: &LatestCompile, range: Option<&HeaderValue>) -> HttpResponse {
let proj_base_dir = get_proj_base_dir(&lastest_pdf.project_id);
let pdf_name = format!(
"{}{}",
get_filename_without_ext(&lastest_pdf.file_name),
".pdf"
);
let pdf_file_path = join_paths(&[proj_base_dir, pdf_name]);
if range.is_none() {
let mut file = File::open(pdf_file_path).expect("Failed to open file");
let mut buf = Vec::new();
file.read_to_end(&mut buf);
let metadata = file.metadata().expect("Failed to get metadata");
let file_size = metadata.len();
return HttpResponse::PartialContent()
.insert_header(CacheControl(vec![CacheDirective::NoCache]))
.append_header(("Accept-Ranges", "bytes"))
.append_header(("Content-Length", file_size))
.append_header((
"Access-Control-Expose-Headers",
"Accept-Ranges,Content-Range",
))
.content_type("application/pdf")
.body(buf);
}
let range_value = range.unwrap().to_str().unwrap();
warn!("range_value {}", range_value);
let bytes_info: Vec<&str> = range.unwrap().to_str().unwrap().split("=").collect();
let mut parts = bytes_info[1].split('-');
let start = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
warn!("get the start {}", start);
let end = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
warn!("get the end {}", end);
let mut file = File::open(pdf_file_path).expect("Failed to open file");
let metadata = file.metadata().expect("Failed to get metadata");
let file_size = metadata.len();
file.seek(SeekFrom::Start(start))
.expect("Failed to seek file");
let mut buf = vec![0; (end - start + 1) as usize];
file.take(end - start + 1)
.read_exact(&mut buf)
.expect("Failed to read file");
let content_range = format!("bytes {}-{}/{}", start, end, file_size);
return HttpResponse::PartialContent()
.insert_header(CacheControl(vec![CacheDirective::NoCache]))
.append_header(("Content-Range", content_range))
.append_header(("Accept-Ranges", "bytes"))
.append_header(("Content-Length", file_size))
.append_header((
"Access-Control-Expose-Headers",
"Accept-Ranges,Content-Range",
))
.content_type("application/pdf")
.body(buf);
}
在第一个请求中,客户端将发送一个不带范围标头的请求,服务器返回标头
Accept-Ranges
等并告诉客户端服务器支持范围请求,然后客户端将切换到范围请求以加载其余部分pdf内容。希望这会对您有所帮助。您还可以配置在后端获取整个 pdf 或仅预下载 pdf 的一部分。