Mam jednostronną aplikację internetową opartą na jquery. Komunikuje się z usługą internetową RESTful za pośrednictwem połączeń AJAX.
Próbuję wykonać następujące czynności:
- Prześlij POST zawierający dane JSON do adresu URL REST.
- Jeśli żądanie określa odpowiedź JSON, zwracana jest JSON.
- Jeśli żądanie określa odpowiedź w formacie PDF / XLS / etc, zwracany jest plik binarny do pobrania.
Mam teraz 1 i 2, a aplikacja kliencka jquery wyświetla zwrócone dane na stronie WWW, tworząc elementy DOM na podstawie danych JSON. Mam również # 3 działający z punktu widzenia usługi sieciowej, co oznacza, że utworzy i zwróci plik binarny, jeśli otrzyma prawidłowe parametry JSON. Ale nie jestem pewien, jak najlepiej poradzić sobie z numerem 3 w kodzie javascript klienta.
Czy można odzyskać plik do pobrania z takiego wywołania ajax? Jak mogę pobrać i zapisać plik w przeglądarce?
$.ajax({
type: "POST",
url: "/services/test",
contentType: "application/json",
data: JSON.stringify({category: 42, sort: 3, type: "pdf"}),
dataType: "json",
success: function(json, status){
if (status != "success") {
log("Error loading data");
return;
}
log("Data loaded!");
},
error: function(result, status, err) {
log("Error loading data");
return;
}
});
Serwer odpowiada następującymi nagłówkami:
Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)
Innym pomysłem jest wygenerowanie pliku PDF i zapisanie go na serwerze i zwrócenie JSON, który zawiera adres URL pliku. Następnie wywołaj kolejne wywołanie w module obsługi sukcesu ajax, aby zrobić coś takiego:
success: function(json,status) {
window.location.href = json.url;
}
Ale robienie tego oznacza, że musiałbym wykonać więcej niż jedno połączenie z serwerem, a mój serwer musiałby budować pliki do pobrania, przechowywać je gdzieś, a następnie okresowo czyścić ten obszar pamięci.
Musi istnieć prostszy sposób na osiągnięcie tego. Pomysły?
EDYCJA: Po przejrzeniu dokumentacji dla $ .ajax, widzę, że typ danych odpowiedzi może być tylko jeden xml, html, script, json, jsonp, text
, więc zgaduję, że nie ma sposobu na bezpośrednie pobranie pliku za pomocą żądania ajax, chyba że osadzę plik binarny przy użyciu Schemat URI danych, jak sugerowano w odpowiedzi @VinayC (nie jest to coś, co chcę zrobić).
Sądzę więc, że moje opcje to:
Nie używaj ajax, a zamiast tego prześlij formularz formularza i umieść moje dane JSON w wartościach formularza. Prawdopodobnie musiałby zadzierać z ukrytymi ramkami iframe i tak dalej.
Nie używaj ajax i zamiast tego przekonwertuj moje dane JSON na ciąg zapytania, aby zbudować standardowe żądanie GET i ustawić window.location.href na ten adres URL. Może być konieczne użycie event.preventDefault () w moim module obsługi kliknięć, aby przeglądarka nie zmieniła adresu URL aplikacji.
Użyj mojego innego pomysłu powyżej, ale wzbogaconego sugestiami z odpowiedzi @naikus. Prześlij żądanie AJAX z pewnym parametrem, który informuje serwis internetowy o wywołaniu za pośrednictwem wywołania ajax. Jeśli usługa internetowa jest wywoływana z wywołania ajax, po prostu zwróć JSON z adresem URL do wygenerowanego zasobu. Jeśli zasób jest wywoływany bezpośrednio, zwróć rzeczywisty plik binarny.
Im więcej o tym myślę, tym bardziej podoba mi się ostatnia opcja. W ten sposób mogę odzyskać informacje o żądaniu (czas wygenerowania, rozmiar pliku, komunikaty o błędach itp.) I mogę działać na podstawie tych informacji przed rozpoczęciem pobierania. Minusem jest dodatkowe zarządzanie plikami na serwerze.
Jakieś inne sposoby na osiągnięcie tego? Jakieś zalety / wady tych metod, o których powinienem wiedzieć?
źródło
url = 'http://localhost/file.php?file='+ $('input').val(); window.open(url);
Prosty sposób na uzyskanie tych samych rezultatów. Umieść nagłówek w pliku php. Nie trzeba wysyłać żadnychOdpowiedzi:
Rozwiązanie letronje działa tylko dla bardzo prostych stron.
document.body.innerHTML +=
pobiera tekst HTML treści, dołącza ramkę HTML iframe i ustawia innerHTML strony na ten ciąg. Spowoduje to usunięcie m.in. powiązań zdarzeń na stronie. Utwórz element i użyjappendChild
zamiast niego.Lub używając jQuery
Co to właściwie robi: wykonaj post w pliku /create_binary_file.php z danymi w zmiennej postData; jeśli ten post zakończy się powodzeniem, dodaj nową ramkę iframe do treści strony. Zakłada się, że odpowiedź z /create_binary_file.php będzie zawierać wartość „url”, czyli adres URL, z którego można pobrać wygenerowany plik PDF / XLS / etc. Dodanie ramki iframe do strony, która odwołuje się do tego adresu URL, spowoduje, że przeglądarka zachęci użytkownika do pobrania pliku, przy założeniu, że serwer WWW ma odpowiednią konfigurację typu MIME.
źródło
+=
i odpowiednio zaktualizuję moją aplikację. Dzięki!Resource interpreted as Document but transferred with MIME type application/pdf
. Następnie ostrzega mnie, że plik może być niebezpieczny. Jeśli odwiedzę bezpośrednio retData.url, nie będzie żadnych ostrzeżeń ani problemów.Bawiłem się z inną opcją, która wykorzystuje obiekty BLOB. Udało mi się go pobrać do pobierania dokumentów tekstowych i pobrałem pliki PDF (jednak są one uszkodzone).
Za pomocą interfejsu API obiektów blob można wykonać następujące czynności:
To jest IE 10+, Chrome 8+, FF 4+. Zobacz https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL
Plik zostanie pobrany tylko w Chrome, Firefox i Opera. Używa atrybutu pobierania tagu zakotwiczenia, aby zmusić przeglądarkę do pobrania.
źródło
click
:window.URL.revokeObjectURL(link.href);
Znam tego rodzaju stare, ale myślę, że wymyśliłem bardziej eleganckie rozwiązanie. Miałem dokładnie ten sam problem. Problem, który miałem z sugerowanymi rozwiązaniami, polegał na tym, że wszystkie wymagały zapisania pliku na serwerze, ale nie chciałem zapisywać plików na serwerze, ponieważ wprowadził inne problemy (bezpieczeństwo: dostęp do pliku można było uzyskać przez nieautoryzowani użytkownicy, czyszczenie: jak i kiedy pozbywasz się plików). I podobnie jak ty, moje dane były złożone, zagnieżdżone obiekty JSON, które trudno byłoby umieścić w formie.
Zrobiłem dwie funkcje serwera. Pierwszy zweryfikował dane. Jeśli wystąpił błąd, zostanie on zwrócony. Jeśli to nie był błąd, zwróciłem wszystkie parametry zserializowane / zakodowane jako ciąg base64. Następnie na kliencie mam formularz, który ma tylko jedno ukryte wejście i posty na drugiej funkcji serwera. Ustawiam ukryte dane wejściowe na ciąg base64 i przesyłam format. Druga funkcja serwera dekoduje / deserializuje parametry i generuje plik. Formularz może zostać przesłany do nowego okna lub ramki iframe na stronie, a plik się otworzy.
W grę wchodzi trochę więcej pracy i być może trochę więcej przetwarzania, ale ogólnie z tym rozwiązaniem czułem się znacznie lepiej.
Kod jest w C # / MVC
na kliencie
źródło
Jest prostszy sposób, stwórz formularz i opublikuj go, ryzykuje to zresetowaniem strony, jeśli typ mime powrotu jest czymś, co otworzy przeglądarka, ale dla csv i takie jest idealne
Przykład wymaga podkreślenia i jquery
W przypadku html, tekstu itp. Upewnij się, że typ mimetyczny to coś takiego jak application / octet-stream
kod php
źródło
Krótko mówiąc, nie ma prostszego sposobu. Musisz wykonać kolejne żądanie serwera, aby wyświetlić plik PDF. Chociaż istnieje kilka alternatyw, ale nie są one idealne i nie będą działać na wszystkich przeglądarkach:
źródło
Content-Disposition: attachment;filename="file.txt"
), ale naprawdę chcę wypróbować to podejście następnym razem.URI
Wcześniej korzystałem z danych dla miniatur, ale nigdy nie przyszło mi do głowy, aby używać ich do pobierania - dziękuję za udostępnienie tego samorodka.Minęło trochę czasu, odkąd zadano to pytanie, ale miałem takie samo wyzwanie i chcę podzielić się moim rozwiązaniem. Wykorzystuje elementy z innych odpowiedzi, ale nie byłem w stanie go znaleźć w całości. Nie używa formularza ani ramki iframe, ale wymaga pary żądań post / get. Zamiast zapisywać plik między żądaniami, zapisuje dane wpisu. Wydaje się być zarówno prosty, jak i skuteczny.
klient
serwer
źródło
źródło
Nie do końca odpowiedź na oryginalny post, ale szybkie i brudne rozwiązanie do publikowania obiektu json na serwerze i dynamicznego generowania pobierania.
JQuery po stronie klienta:
.. a następnie dekodowanie łańcucha json na serwerze i ustawianie nagłówków do pobrania (przykład PHP):
źródło
Myślę, że najlepszym rozwiązaniem jest użycie kombinacji. Twoje drugie podejście wydaje się być eleganckim rozwiązaniem, w które zaangażowane są przeglądarki.
Tak więc w zależności od sposobu wykonania połączenia. (niezależnie od tego, czy jest to przeglądarka, czy wywołanie usługi internetowej), możesz użyć ich kombinacji, wysyłając adres URL do przeglądarki i wysyłając surowe dane do dowolnego innego klienta usługi internetowej.
źródło
Nie śpię już od dwóch dni, próbując dowiedzieć się, jak pobrać plik za pomocą jquery z wywołaniem ajax. Całe wsparcie, które otrzymałem, nie mogło pomóc w mojej sytuacji, dopóki nie spróbuję tego.
Strona klienta
Po stronie serwera
Powodzenia.
źródło
Znalazłem go dawno temu i działa idealnie!
źródło
Innym podejściem zamiast zapisywania pliku na serwerze i pobierania go, jest użycie .NET 4.0+ ObjectCache z krótkim terminem ważności do drugiej akcji (w tym czasie można go ostatecznie zrzucić). Powodem, dla którego chcę użyć JQuery Ajax do wykonania połączenia, jest to, że jest on asynchroniczny. Zbudowanie mojego dynamicznego pliku PDF zajmuje sporo czasu i w tym czasie wyświetlam okno dialogowe zajętego pokrętła (pozwala również na wykonanie innej pracy). Podejście polegające na wykorzystaniu danych zwróconych w „sukcesie” do utworzenia obiektu Blob nie działa niezawodnie. To zależy od zawartości pliku PDF. Łatwo jest go zepsuć dane w odpowiedzi, jeśli nie jest ono całkowicie tekstowe, a to wszystko, co Ajax może obsłużyć.
źródło
Rozwiązanie
Wydaje mi się, że załącznik Content-Disposition działa:
Obejście
application / octet-stream
Z JSONem działo się coś podobnego, dla mnie po stronie serwera ustawiałem nagłówek na self.set_header („Content-Type”, „ application / json ”), ale kiedy zmieniłem go na:
Automatycznie go pobrał.
Wiedz również, że aby plik zachował sufiks .json, musisz to zrobić w nagłówku nazwy pliku:
źródło
Dzięki HTML5 możesz po prostu utworzyć kotwicę i kliknąć ją. Nie ma potrzeby dodawania go do dokumentu jako dziecko.
Wszystko gotowe.
Jeśli chcesz mieć specjalną nazwę do pobrania, przekaż ją w
download
atrybucie:źródło