Wymuś otwarcie wyskakującego okienka „Zapisz jako…” po kliknięciu łącza tekstowego, aby wyświetlić plik PDF w formacie HTML

173

Na mojej stronie internetowej mam kilka dużych katalogów w formacie PDF i muszę je połączyć w celu pobrania. Kiedy szukałem w Google, znalazłem coś takiego, co zostało opisane poniżej. Powinien otworzyć wyskakujące okienko „ Zapisz jako ... ” po kliknięciu linku ...

 <head>
    <meta name="content-disposition" content="inline; filename=filename.pdf">
    ...

Ale to nie działa: / Kiedy łącze do pliku, jak poniżej, po prostu łączy się z plikiem i próbuje otworzyć plik.

    <a href="filename.pdf" title="Filie Name">File name</a>

UPDATE (zgodnie z odpowiedziami poniżej):

Jak widzę, nie ma na to w 100% niezawodnego rozwiązania dla różnych przeglądarek. Prawdopodobnie najlepszym sposobem jest skorzystanie z jednej z poniższych usług internetowych i podanie linku do pobrania ...

projektant próbuje kodować
źródło
1
nie podawaj typu MIME dla plików pdf.
bhups
Wypróbowałem zaktualizowane rozwiązanie, artmania - ale pojawił się ten sam problem, który miałem w Safari. Dostaję coś, co wygląda jak plik PDF w oknie przeglądarki i dopiero po kliknięciu zakładki „podgląd” lub „pobierz” na dole pojawia się funkcja wyszukiwania, której tak bardzo potrzebuję.
heathwaller
bez kliknięcia: stackoverflow.com/questions/2598658/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
podobny, ale mniej specyficzny dla typu pliku: stackoverflow.com/questions/1465573/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

267

Od odpowiedzi do Wymuś zapisanie pliku przez przeglądarkę po kliknięciu linku :

<a href="path/to/file" download>Click here to download</a>
Ayush Gupta
źródło
17
W chwili pisania tego komentarza downloadatrybut jest ograniczony do Chrome, Firefox i Opera. Nawet najnowsze wersje IE i Safari go nie obsługują. Aby uzyskać pomoc w przyszłości: sprawdź caniuse.com/#feat=download !
Sygmoral,
Sprawdzanie błędów Netbeans narzeka na to, ale wydaje się, że działa dobrze. Możesz określić wstępną nazwę nowego pliku, na przykład: download = "myFile.txt"
Yster,
1
Działa to w Edge w momencie pisania tego komentarza i wydaje się być jedynym sposobem na zatrzymanie otwierania hiperłączy PDF w bardzo kiepskiej próbie Edge'a w przeglądarce PDF.
W chwili pisania tego komentarza testowałem go na Chrome, Opera, Edge, Mozilla. Wszystkie najnowsze. Działa na wszystkich oprócz Mozilli.
SI
7
Działa to tylko w przypadku linków tego samego pochodzenia, jak wspomniano na caniuse.com/#feat=download . Jeśli Twoje linki są z różnych źródeł, jedyną opcją (na razie) jest wymuszenie typu odpowiedzi na „application / octet-stream”, jak sugeruje wiele odpowiedzi. Jeśli nie masz dostępu do serwera, możesz spróbować go proxy i ręcznie ustawić nagłówek odpowiedzi.
jean-baptiste
80
<a href="file link" download target="_blank">Click here to download</a>

U mnie działa w Firefoksie i Chrome.

DrWaky
źródło
2
Jest to jednak dokładnie to samo, co aktualna najlepsza odpowiedź od Ayush Gupta w 2013 roku. target="_blank"Może jednak sprawić, że będzie nieco bardziej użyteczna w nieobsługiwanych przeglądarkach (pliki PDF są następnie otwierane w nowym oknie / karcie, zamiast nadpisywać bieżącą stronę).
Sygmoral
3
Dla mnie target="_blank"było to konieczne, ponieważ downloadtylko nie udało się poprawnie uruchomić pobierania (Chrome)
casraf
2
Jeśli w downloadatrybucie podasz ciąg znaków , będzie on używany jako nazwa pliku. Cały czas używam go w skryptach użytkownika.
slabynespase
34

Metatagi nie są niezawodnym sposobem osiągnięcia tego wyniku. Generalnie nie powinieneś tego nawet robić - decyzję, co zrobić z dostarczoną przez Ciebie treścią, należy pozostawić użytkownikowi / agentowi użytkownika. Użytkownik może zawsze zmusić swoją przeglądarkę do pobrania pliku, jeśli chce.

Jeśli nadal chcesz zmusić przeglądarkę do pobrania pliku, zmodyfikuj bezpośrednio nagłówki HTTP. Oto przykład kodu PHP:

$path = "path/to/file.pdf";
$filename = "file.pdf";
header('Content-Transfer-Encoding: binary');  // For Gecko browsers mainly
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($path)) . ' GMT');
header('Accept-Ranges: bytes');  // Allow support for download resume
header('Content-Length: ' . filesize($path));  // File size
header('Content-Encoding: none');
header('Content-Type: application/pdf');  // Change the mime type if the file is not PDF
header('Content-Disposition: attachment; filename=' . $filename);  // Make the browser display the Save As dialog
readfile($path);  // This is necessary in order to get it to actually download the file, otherwise it will be 0Kb

Zauważ, że jest to tylko rozszerzenie protokołu HTTP; niektóre przeglądarki i tak mogą go zignorować.

Karel Petranek
źródło
4
W praktyce uważam, że jest to szeroko stosowane.
Matthew Wilson,
To najlepsze rozwiązanie, bezproblemowe i działa. Możesz bezpośrednio pobrać dowolny typ pliku za pomocą tej metody. +1
casraf
3
Content-Transfer-Encoding nie istnieje w HTTP. Kodowanie treści: żadne nie jest bezużyteczne.
Julian Reschke
ZAZNACZ w swojej odpowiedzi, że JEŻELI plik pochodzi z zewnętrznego serwera / domeny, to ta odpowiedź może nie być rozwiązaniem ...
T.Todua
Chciałbym dodać, że na niektórych serwerach ten lub podobny kod nie działa, gdy pliki stają się duże (> 80 MB w moim przypadku). Używanie ob_end_flush()też nie pomogło.
Hendrik
22

Miałem ten sam problem i znalazłem rozwiązanie, które do tej pory działało świetnie. Umieszczasz następujący kod w swoim .htaccesspliku:

<FilesMatch "\.(?i:pdf)$">
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</FilesMatch>

Pochodzi z wymuszania pobrania pliku zamiast wyświetlania go w przeglądarce .

Tony
źródło
4
To działało dobrze, ponieważ miałem określony katalog dokumentów do pobrania. Pominąłem ForceType, aby typy pozostały takie, jakie są. Nie potrzebowałem też rozróżniania wielkości liter; w moim zdaje się już nie rozróżniać wielkości liter. Dodałem kilka dodatkowych typów, w tym opcjonalne-x dla starych / nowych rozszerzeń pakietu Office:<FilesMatch "\.(pdf|xlsx?|docx?)$">
goodeye
Działało to wspaniale, nawet w OSX Safari, gdzie inne odpowiedzi (w tym zaakceptowana, jak wskazał tam jeden z komentatorów) są ograniczone do niektórych przeglądarek obsługujących daną funkcję HTML. Ten wydaje mi się bardziej idealny, ponieważ działał na wszystkich płaszczyznach, chociaż wymaga wyższego poziomu dostępu do plików konfiguracyjnych serwera.
bwright
Możesz to również umieścić w swoim pliku apache.conf, nie używam .htaccess, ponieważ zajmuje więcej zasobów.
Michael Fever,
W jaki sposób witryna internetowa hostowana przez system Windows mogłaby to zaimplementować, skoro nie mogą odczytać plików htaccess?
PoloHoleSet
9

Znalazłem bardzo proste rozwiązanie dla Firefoksa (działa tylko z względnym, a nie bezpośrednim href): dodaj type="application/octet-stream":

<a href="./file.pdf" id='example' type="application/octet-stream">Example</a>
Użytkownik
źródło
1
@Martin Chrome w systemie operacyjnym Chrome i Firefox w systemie Linux. Okazuje się, że plik musi być lokalny, aby działał. Mój href był adresem URL do przechowywania w chmurze.
ttfreeman
7

Spróbuj dodać tę linię do swojego pliku .htaccess.

AddType application/octet-stream .pdf

Mam nadzieję, że będzie działać, ponieważ jest niezależny od przeglądarki.

Sharad Gautam
źródło
Chciałbym wiedzieć, dlaczego zostało to odrzucone - jest to jedna z najprostszych metod i działa: css-tricks.com/snippets/htaccess/ ...
Nathan Hornby
Ludzie próbują modyfikować podstawową funkcjonalność serwera na darmowych witrynach hostingowych, a następnie głosują negatywnie, ponieważ nie mogą zmusić go do działania. Każde inne rozwiązanie będzie miało zastrzeżenia, ponieważ jest to „hack”, aby to zastąpić.
Opuszczony
Odciskam to, ponieważ jest to niewłaściwe rozwiązanie problemu. To zły pomysł, aby bawić się różnymi rodzajami zasobów.
Brad
@NathanHornby - nie przegłosowałem tego, ale to nie zadziała dla żadnej witryny hostowanej przez system Windows, ponieważ nie używają one .htaccess.
PoloHoleSet
Nie głosowałem przeciw, ale to rozwiązanie ślepo wpływa na WSZYSTKIE pliki z tym przyrostkiem. Jest to bezkrytyczne i dlatego nie będzie odpowiednie we wszystkich przypadkach.
JBH
6

Zwykle tak się dzieje, ponieważ niektóre ustawienia przeglądarek lub wtyczki otwierają plik PDF bezpośrednio w tym samym oknie, co zwykła strona internetowa.

Poniższe mogą ci pomóc. Zrobiłem to w PHP kilka lat temu. Ale obecnie nie pracuję na tej platformie.

<?php
    if (isset($_GET['file'])) {
        $file = $_GET['file'];
        if (file_exists($file) && is_readable($file) && preg_match('/\.pdf$/',$file)) {
            header('Content-type: application/pdf');
            header("Content-Disposition: attachment; filename=\"$file\"");
            readfile($file);
        }
    }
    else {
        header("HTTP/1.0 404 Not Found");
        echo "<h1>Error 404: File Not Found: <br /><em>$file</em></h1>";
    }
?>

Zapisz powyższe jako download.php .

Zapisz ten mały fragment kodu jako plik PHP gdzieś na swoim serwerze i możesz go użyć do pobrania pliku w przeglądarce, zamiast wyświetlać go bezpośrednio. Jeśli chcesz udostępniać pliki inne niż PDF, usuń lub edytuj wiersz 5.

Możesz go używać w ten sposób:

Dodaj poniższe łącze do pliku HTML.

<a href="download.php?file=my_pdf_file.pdf">Download the cool PDF.</a>

Źródła: ten blog

Ashay
źródło
7
Wygląda na to, że jest to dobry sposób na udostępnienie każdego pliku na serwerze każdemu, kto zwraca na to uwagę. download.php?file=database_password_file.phpna przykład.
pbarney
5

Po prostu umieść poniższy kod w swoim .htaccesspliku:

AddType application/octet-stream .csv
AddType application/octet-stream .xls
AddType application/octet-stream .doc
AddType application/octet-stream .avi
AddType application/octet-stream .mpg
AddType application/octet-stream .mov
AddType application/octet-stream .pdf

Możesz też zrobić sztuczkę za pomocą JavaScript

element.setAttribute( 'download', whatever_string_you_want);
manish1706
źródło
1
Czy to nie zakłada, że ​​serwer WWW to Apache ?
Peter Mortensen
5

Właśnie tego użyłem, ale nie wiem, czy działa we wszystkich przeglądarkach.

Działa w przeglądarce Firefox:

<a href="myfile.pdf" download>Click to Download</a>
NowLiveLove
źródło
1
Jest to sugerowane, wraz z dodatkowymi informacjami, we wcześniejszej odpowiedzi .
usr2564301
4

naprawdę prosty sposób aby to osiągnąć, bez korzystania z zewnętrznych witryn pobierania lub modyfikację nagłówków itd. Jest po prostu utworzyć plik ZIP z wnętrza PDF oraz link bezpośrednio do pliku ZIP. Spowoduje to ZAWSZE wywołanie okna dialogowego Zapisz / Otwórz i nadal łatwo jest dwukrotnie kliknąć okna PDF, w których uruchamiany jest program skojarzony z plikiem .zip.

Przy okazji świetne pytanie, ja też szukałem odpowiedzi, ponieważ większość wtyczek PDF osadzonych w przeglądarce zajmuje bardzo dużo czasu, zanim cokolwiek wyświetli (i często zawiesza przeglądarkę podczas ładowania pliku PDF).

Panie Bungle
źródło
31
Straszna użyteczność. Wielu użytkowników końcowych nie wiedziałoby, co zrobić z plikiem zip.
CodeWarrior,
1
Czasami proste rozwiązania są najlepszym rozwiązaniem!
elrado
1
Nie jest to dobre rozwiązanie. Mam dokładnie ten problem z Firefoksem i plikiem ZIP. Co powinienem zrobić? Skompresować plik? W takim przypadku modyfikacja nagłówków HTTP może być rozwiązaniem, ale nie mam do tego dostępu.
Arnaud Weil
2

Rozwiązanie po stronie serwera jest bardziej kompatybilne, dopóki atrybut „download” nie zostanie zaimplementowany we wszystkich przeglądarkach.

Jednym z przykładów w Pythonie może być niestandardowa procedura obsługi żądań HTTP dla magazynu plików. Linki wskazujące na magazyn plików są generowane w następujący sposób:

http://www.myfilestore.com/filestore/13/130787e71/download_as/desiredName.pdf

Oto kod:

class HTTPFilestoreHandler(SimpleHTTPRequestHandler):

    def __init__(self, fs_path, *args):
        self.fs_path = fs_path                          # Filestore path
        SimpleHTTPRequestHandler.__init__(self, *args)

    def send_head(self):
        # Overwrite SimpleHTTPRequestHandler.send_head to force download name
        path = self.path
        get_index = (path == '/')
        self.log_message("path: %s" % path)
        if '/download_as/' in path:
            p_parts = path.split('/download_as/')
            assert len(p_parts) == 2, 'Bad download link:' + path
            path, download_as = p_parts
        path = self.translate_path(path )
        f = None
        if os.path.isdir(path):
            if not self.path.endswith('/'):
                # Redirect browser - doing basically what Apache does
                self.send_response(301)
                self.send_header("Location", self.path + "/")
                self.end_headers()
                return None
            else:
                return self.list_directory(path)
        ctype = self.guess_type(path)
        try:
            f = open(path, 'rb')
        except IOError:
            self.send_error(404, "File not found")
            return None
        self.send_response(200)
        self.send_header("Content-type", ctype)
        fs = os.fstat(f.fileno())
        self.send_header("Expires", '0')
        self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
        self.send_header("Cache-Control", 'must-revalidate, post-check=0, pre-check=0')
        self.send_header("Content-Transfer-Encoding", 'binary')
        if download_as:
            self.send_header("Content-Disposition", 'attachment; filename="%s"' % download_as)
        self.send_header("Content-Length", str(fs[6]))
        self.send_header("Connection", 'close')
        self.end_headers()
        return f


class HTTPFilestoreServer:

    def __init__(self, fs_path, server_address):
        def handler(*args):
            newHandler = HTTPFilestoreHandler(fs_path, *args)
            newHandler.protocol_version = "HTTP/1.0"
        self.server = BaseHTTPServer.HTTPServer(server_address, handler)

    def serve_forever(self, *args):
        self.server.serve_forever(*args)


def start_server(fs_path, ip_address, port):
    server_address = (ip_address, port)
    httpd = HTTPFilestoreServer(fs_path, server_address)

    sa = httpd.server.socket.getsockname()
    print "Serving HTTP on", sa[0], "port", sa[1], "..."
    httpd.serve_forever()
yucer
źródło
2

Bardzo prostym sposobem na to, jeśli chcesz wymusić pobieranie pojedynczego linku na swojej stronie, jest użycie atrybutu pobierania HTML5 w linku href.

Zobacz: http://davidwalsh.name/download-attribute

dzięki temu możesz zmienić nazwę pliku, który użytkownik pobierze, a jednocześnie wymusi pobieranie.

Odbyła się debata, czy to dobra praktyka, czy nie, ale w moim przypadku mam wbudowaną przeglądarkę do pliku PDF, a przeglądarka nie oferuje linku do pobrania, więc muszę udostępnić go osobno. Tutaj chcę się upewnić, że użytkownik nie otworzy pliku PDF w przeglądarce internetowej, co byłoby mylące.

Nie będzie to konieczne, aby otworzyć okno dialogowe Zapisz jako, ale spowoduje pobranie łącza bezpośrednio do wstępnie ustawionego miejsca docelowego pobierania. Oczywiście, jeśli robisz witrynę dla kogoś innego i chcesz, aby ręcznie wpisywali atrybuty swoich linków, jest to prawdopodobnie zły pomysł, ale jeśli istnieje sposób na umieszczenie atrybutu w linkach, może to być lekkie rozwiązanie.

JJxyz
źródło
1

To jest stary post, ale tutaj jest moje rozwiązanie w JavaScript wykorzystujące bibliotekę jQuery.

<script>
(function($){
    var download = [];
    $('a.force-download, .force-download a').each(function(){
        // Collect info
        var $this = $(this),
            $href = $this.attr('href'),
            $split = $href.split('/'),
            $name = document.title.replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-'); // get title and clean it for the URL

        // Get filename from URL
        if($split[($split.length-1)])
        {
            $tmp = $split[($split.length-1)];
            $tmp = $tmp.split('.');
            $name = $tmp[0].replace(/[\W_]/gi, '-').replace(/-{2,}/g, '-');
        }

        // If name already exists, put timestamp there
        if($.inArray($name, download) > -1)
        {
            $name = $name + '-' + Date.now().replace(/[\W]/gi, '-');
        }

        $(this).attr("download", $name);
        download.push($name);
    });
}(jQuery || window.jQuery))
</script>

Wystarczy, że użyjesz class force-downloadw swoim <a>tagu i wymusisz pobieranie automatycznie. Możesz również dodać go do rodzica divi odebrać wszystkie zawarte w nim linki.

Przykład:

<a href="/some/good/url/Post-Injection_Post-Surgery_Instructions.pdf" class="force-download" target="_blank">Download PDF</a>

Jest to świetne rozwiązanie dla WordPress i wszelkich innych systemów lub niestandardowych witryn internetowych.

Ivijan Stefan Stipić
źródło
0

Po nazwie pliku w kodzie HTML dodaję ?forcedownload=1

To był dla mnie najprostszy sposób na wywołanie okna dialogowego w celu zapisania lub pobrania.

Bonnie
źródło
0

Jeśli masz wtyczkę w przeglądarce, która wie, jak otworzyć plik PDF, otworzy się ona bezpośrednio. Podobnie jak w przypadku obrazów i treści HTML.

Zatem alternatywnym podejściem nie jest wysyłanie w odpowiedzi typu MIME. W ten sposób przeglądarka nigdy nie będzie wiedzieć, która wtyczka powinna ją otworzyć. W związku z tym wyświetli okno dialogowe Zapisz / Otwórz .

sushil bharwani
źródło
Jest to trudne do osiągnięcia, ponieważ to serwer wysyła typ MIME, a podczas bezpośredniego łączenia z plikiem PDF nie można zmienić nagłówków.
Karel Petranek
0

Właśnie miałem bardzo podobny problem z dodanym problemem, który potrzebowałem do tworzenia linków do pobierania plików w pliku ZIP.

Najpierw próbowałem utworzyć plik tymczasowy, a następnie podałem łącze do pliku tymczasowego, ale stwierdziłem, że niektóre przeglądarki po prostu wyświetlają zawartość (plik CSV Excel) zamiast oferować pobranie. Ostatecznie znalazłem rozwiązanie za pomocą serwletu. Działa zarówno na Tomcat, jak i GlassFish, a wypróbowałem go w Internet Explorerze 10 i Chrome.

Aplet przyjmuje jako dane wejściowe pełną nazwę ścieżki do pliku ZIP oraz nazwę pliku znajdującego się w pliku ZIP, który powinien zostać pobrany.

W moim pliku JSP mam tabelę wyświetlającą wszystkie pliki wewnątrz zip, z linkami, które mówią: onclick='download?zip=<%=zip%>&csv=<%=csv%>'

Kod serwletu znajduje się w download.java:

package myServlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.zip.*;
import java.util.*;

// Extend HttpServlet class
public class download extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        PrintWriter out = response.getWriter(); // now we can write to the client

        String filename = request.getParameter("csv");
        String zipfile = request.getParameter("zip");

        String aLine = "";

        response.setContentType("application/x-download");
        response.setHeader( "Content-Disposition", "attachment; filename=" + filename); // Force 'save-as'
        ZipFile zip = new ZipFile(zipfile);
        for (Enumeration e = zip.entries(); e.hasMoreElements();) {
            ZipEntry entry = (ZipEntry) e.nextElement();
            if(entry.toString().equals(filename)) {
                InputStream is = zip.getInputStream(entry);
                BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 65536);
                while ((aLine = br.readLine()) != null) {
                    out.println(aLine);
                }
                is.close();
                break;
            }
        }
    }
}

Aby skompilować na Tomcat, potrzebujesz ścieżki klas zawierającej tomcat \ lib \ servlet-api.jar lub na GlassFish: glassfish \ lib \ j2ee.jar

Ale każdy z nich będzie działał na obu. Musisz także ustawić serwlet na web.xml.

mljm
źródło
0

Dodaj nagłówek odpowiedzi Content-Disposition: załącznik; po którym następuje nazwa pliku. Usuń Meta Content-Disposition; Inline; który otworzy dokument w tym samym oknie

W java jest ustawiony jako

response.setHeader("Content-Disposition", "attachment;filename=test.jpg");
johnmin
źródło
-2

W przypadku dużych plików PDF przeglądarka się zawiesza. W Mozilli menu NarzędziaOpcjeAplikacje , a następnie obok typu zawartości dokumentu Adobe Acrobat. Z listy rozwijanej Akcja wybierz Zawsze pytaj .

To nie zadziałało, więc zadziałało:

Menu Narzędzia * → DodatkiAdobe Acrobat (wtyczka Adobe PDF do przeglądarki Firefox) → WYŁĄCZ. Teraz mogę pobierać e-booki!

Joojoo
źródło
2
przeczytałeś pytanie? to nie jest odpowiedź między przeglądarkami
Mark
1
@mark to także instrukcja dla użytkownika, który ma monitować o wymuszone pobieranie plików PDF, a nie właściciela witryny.
Nathan Hornby