类似于这个个问题的反面。
上有的网站时,遇到要下载的非纯文本文件,eww默认的行为是显示(需要mailcap)。
但我不希望它显示,而是希望它下载。
通过上面的问题,我想可能的方式是把这些文件设置为application/force-download
。
P.S. eww-download
的默认行为。。。不知道下载下来的网页文件名到底是怎么定的。。。
类似于这个个问题的反面。
上有的网站时,遇到要下载的非纯文本文件,eww默认的行为是显示(需要mailcap)。
但我不希望它显示,而是希望它下载。
通过上面的问题,我想可能的方式是把这些文件设置为application/force-download
。
P.S. eww-download
的默认行为。。。不知道下载下来的网页文件名到底是怎么定的。。。
PDFs are viewed inline, by default, with
doc-view-mode
, but this can be customized by using the mailcap (see mailcap) mechanism, in particularmailcap-mime-data
.
macilcap 是 Emacs 自帯的 MIME lib 的一部分,你是不是有什麼误解,以为是要另装东西。
Emacs 的 MIME 会 parse ~/.mailcap
出一个 mailcap-mime-data
alist,不过你是要 eww 下载,要在 Emacs 里用 mailcap-user-mime-data
override。
为了给你节约時間 figure out 要怎么写,
(setq mailcap-user-mime-data
'(((viewer . mailcap-save-binary-file)
(type . "application/pdf")) ;; obviously, this is pdf
((viewer . mailcap-save-binary-file)
(type . "image/.+")))) ;; This line is a regex matching "image/..."
思路是这样,不过直接这么来不行。
我最后貌似设置成功的方式长这样:
(dolist (lst mailcap-mime-data)
(when (and (stringp (car lst)) (string-equal "application" (car lst)))
(setcdr lst (mapcan (lambda (elem)
(unless (string-equal (with-output-to-string
(princ (car elem)))
"pdf")
`(,elem)))
(cdr lst)))
(setcdr lst (append (cdr lst) `(("pdf" (viewer . mailcap-save-binary-file)
(type . "application/pdf")))))))
不过还差文件名。。。
你搞错了,mailcap-mime-data 不能直接设置,和mailcap-user-mime-data 格式也不一样,后者是会根据正则对前者修改的。实际上你做的相当于重新发明 mailcap-user-mike-data 的功能。
(mailcap-mime-info "application/pdf")
;; doc-view-mode
(setq mailcap-user-mime-data
'(((viewer . mailcap-save-binary-file)
(type . "application/.+"))))
(mailcap-mime-info "application/pdf")
;; mailcap-save-binary-file
(defun eww-download-callback (status url)
(unless (plist-get status :error)
(let* ((obj (url-generic-parse-url url))
(path (car (url-path-and-query obj)))
(file (eww-make-unique-file-name
(eww-decode-url-file-name (file-name-nondirectory path))
eww-download-directory)))
(goto-char (point-min))
(re-search-forward "\r?\n\r?\n")
(let ((coding-system-for-write 'no-conversion))
(write-region (point) (point-max) file))
(message "Saved %s" file))))
(setq obj (url-generic-parse-url "https://faculty.washington.edu/smcohen/120/SecondOrder.pdf"))
;; #s(url "https" nil nil "faculty.washington.edu" nil "/smcohen/120/SecondOrder.pdf" nil nil t nil t t)
(setq path (car (url-path-and-query obj)))
;; "/smcohen/120/SecondOrder.pdf"
(setq path (file-name-nondirectory path))
;; "SecondOrder.pdf"
我这里这么改mailcap-user-mime-data
的话是nil
,改之前也是nil
。。。
改之前是nil
原因应该是没有图形界面也没有pdf2text
之类的。
那可能是我用的 mac 系统环境自带了 MIME 的关系。
我又试了一下,放配置文件里是没问题的,但单独求值就不行。。。
这个是直链的情况吧,直接按d
就这么下载了。但是不是直链的话,调用mailcap-save-binary-file
会问你文件名。
因为不是直链就是直接传文件数据过来,然后就交由 mime 处理了,不是经过 eww-download 做的。
我说还差文件名就是说这种情况。
这种我觉得不大面积改写就没办法,比如给 mailcap save binary file 加个参数接受文件名,然后自己实现命名。因为这个函数只接受数据没有文件名
有办法在这种情况下获得文件名吗?能的话写个my/eww-save-binary-file
之类的应该就可以了。
我现在倒是很好奇w3m
是怎么获得文件名的了。首先headers
里没有。于是我看w3m
的代码。w3m
获取文件名和获取其它信息是写一块的,不好看懂。
于是我就去查非直链是怎么提供文件名的,结果查到了download
属性。
然而ACM的下载链接没有指定这个属性。。。
同时这个未解决问题提到了cfm
文件。ACM也用的是这个。
w3m wget 下载链接文件的命名都有问题,我建议看 aria2 的实现,这个比较靠谱
我看了一下,获取文件名可能不外乎就是headers
里有、URL里有,或者就是我之前提到的:
不过这个会不会表现在headers
里面我就不知道了,我不是搞HTTP的。
至于为什么eww的不到ACM下载链接文件名,原因在于它没有考虑有一次重定向。ACM的文件名是在重定向的URL里的。。。
获取这个重定向的URL的方法,我采用的是给eww-render
加advice
。具体的做法,看eww-render
的代码就知道了。
(require 'eww)
(defun my/eww-save-binary-file () ".")
(lexical-let* ((filename nil)
(origin eww-download-directory)
(pwd (lambda ()
(substring (shell-command-to-string "pwd") 0 -1)))
(dir-or-pwd (lambda ()
(if (file-exists-p origin) origin (funcall pwd)))))
(let ((aux (lambda (args)
(let ((status (car args)) (url (cadr args)) (rest (cddr args)))
(let* ((cd (assoc "content-disposition" (eww-parse-headers)))
(cd (when cd (cdr cd))))
(setq filename nil)
(when (and cd (string-match "filename=\"\\(.*\\)\"" cd))
(setq filename (match-string 1))))
(let ((redirect (plist-get status :redirect)))
(unless filename
(let* ((o (if redirect redirect url))
(o (url-generic-parse-url o))
(o (car (url-path-and-query o)))
(o (directory-file-name o))
(o (file-name-nondirectory o)))
(setq filename (eww-decode-url-file-name o))))
`(,status ,(if redirect redirect url) ,@rest))))))
(advice-add 'eww-render :filter-args aux)
(advice-add 'eww-download-callback :filter-args aux))
(setq eww-download-directory "~")
(advice-add 'eww-make-unique-file-name :filter-args
(lambda (args)
(let ((file (car args)))
`(,(if (zerop (length file)) "index.html" file)
,(funcall dir-or-pwd)))))
(advice-add 'my/eww-save-binary-file :override
(lambda ()
(goto-char (point-min))
(unwind-protect
(let ((file (read-file-name
"Filename to save as: "
(funcall dir-or-pwd) filename))
(require-final-newline nil))
(write-region (point-min) (point-max) file))
(kill-buffer (current-buffer)))))
(setq mailcap-user-mime-data
'(((viewer . my/eww-save-binary-file) (type . "application/.+")))))
上面的代码在eww-download
和直接进入下载链接时产生作用。下载的目录我的习惯是如果eww-download-directory
不存在,就下到当前文件夹下。
当然,目前的还有点小问题:如果要下载的xxx.pdf
已存在,eww-download
应该是想要把新文件保存为xxx(2).pdf
。但实际上,新的文件被保存成了xxx(2)200
。。。我觉得这不是我的锅。。。
还有,涉及content-disposition
的部分,我没测过。