You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Haskell从URL下载文件仅获取网页源码的问题咨询

原方案有效性说明

你贴的http-conduit实现核心逻辑没有错误,但默认配置下无法覆盖绝大多数公开文件下载场景,这也是你拿到HTML源码而非目标文件的根本原因,具体缺省配置导致的问题有两个:

  • 默认不自动跟随HTTP重定向:绝大多数文件下载站、开源镜像站的公开链接都会先返回301/302/307状态码跳转到真实的文件存储节点,原代码只会获取跳转提示页的HTML内容,不会自动走到最终文件地址。
  • 默认请求不带合法浏览器标识:几乎所有提供大文件下载的站点都会校验请求的User-Agent头,对无标识的脚本请求直接返回访问引导页、人机验证页的HTML源码,不会返回真实文件流。
修正后可直接使用的实现

针对普通压缩包、ISO镜像等公开资源下载,只需要补全上述两个缺省配置,再加上文件写入的原子性保护即可,参考实现如下:

{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Conduit
import Network.HTTP.Types.Header (hUserAgent)
import System.Directory (renameFile)
import System.Process (callCommand)
import qualified System.Process.ListLike as PL (shellEscape)

-- 入参:资源URL、本地保存路径
downloadFile :: String -> FilePath -> IO ()
downloadFile url savePath = do
  initReq <- parseRequest url
  let req = initReq
        { redirectCount = 10 -- 开启重定向跟随,设置10次上限避免跳转死循环
        , requestHeaders =
            [ (hUserAgent, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36")
            ]
        }
  -- 先写入临时文件,下载完成后再重命名,避免中途断网/程序退出产生损坏的目标文件
  runResourceT $ httpSink req $ \_ -> sinkFile "tmp_download_part"
  renameFile "tmp_download_part" savePath

这个版本可以直接覆盖Ubuntu镜像站、普通开源软件压缩包的下载需求。

复杂场景备选方案

如果遇到需要登录鉴权、断点续传、10GB以上超大文件下载的场景,不需要硬写HTTP逻辑,直接选择更成熟的方案即可:

  • 调用系统原生下载工具:通过进程调用wget/curl完成下载,这类工具已经内置了断点续传、重试、重定向、Cookie处理、大文件分块下载的全部逻辑,稳定性远高于手写实现,调用时注意对URL和路径做shell转义避免注入问题,示例调用:callCommand $ "wget -c " ++ PL.shellEscape url ++ " -O " ++ PL.shellEscape savePath
  • 使用绑定libcurl的Haskell库:比如download-curl,底层依赖成熟的libcurl能力,不用手动配置请求参数即可支持绝大多数下载场景,适合不想依赖外部命令行工具的场景。

内容的提问来源于stack exchange,提问作者Thomas Meyer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:24:35