#20.2 — Korzystanie z API: obsługa plików — pobieranie

By 12 March 2015 Kurs Javy

W poprzed­niej częś­ci umieś­cil­iśmy plik na ser­w­erze za pomocą API, dzisi­aj poz­wolimy użytkown­ikom naszej aplikacji wyświ­et­lać / pobier­ać te pliki.

Uwa­ga: opisane tutaj pode­jś­cie nie jest opty­malne i sprawdzi się tylko w przy­pad­ku małych aplikacji lub aplikacji, w których wyma­gana jest kon­tro­la dostepu do tych plików. W przy­pad­ku real­nych aplikacji, które mają obsługi­wać sporo użytkown­ików, poczy­taj o dobrych prak­tykach w dal­szej częś­ci tej lekcji.

Lekcja

Jeśli zre­al­i­zowałaś poprzed­nią lekcję nie będziemy potrze­bowali żad­nych dodatkowych bib­liotek, wystar­czą te, które popral­iśmy poprzednio.

Postępu­je­my ana­log­icznie jak w poprzed­nim przy­pad­ku – prześledźmy najpierw kod kon­trol­era, który pozwala na pobranie jakiegoś pliku:

@RequestMapping(value = "/plik/{nazwa_pliku}", method = RequestMethod.GET)
public void getFile(@PathVariable("nazwa_pliku") String nazwaPliku, HttpServletResponse response) {
    try {
		InputStream is = new FileInputStream(nazwaPliku);
		FileMetadata metedataObject = ...;//tutaj pobieramy dane z bazy danych
		response.setContentType(metadataObject.getContentType());
		response.setContentLength(metadataObject.getSize());
		org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
		response.flushBuffer();
    } catch (IOException ex) {
		log.info("Błąd przy pobieraniu pliku", ex);
		response.setStatus(404);
    }
}

Kon­trol­er ten wyko­rzys­tu­je klasę IOUtils z paki­etu apache com­mons IO, meto­da copy przepisu­je stru­mień wejś­ciowy na stru­mień wyjś­ciowy.  Dzię­ki temu unikamy samodziel­nego pisa­nia pętli.

Weźmy też przykład z doku­men­tacji S3 :

AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider());        
S3Object object = s3Client.getObject(
                  new GetObjectRequest(bucketName, key));
InputStream objectData = object.getObjectContent();

Po połacze­niu obu frag­men­tów otrzy­mu­je­my gotowe do uży­cia rozwiązanie (zwróć uwagę, że zamie­nil­iśmy też sposób przekazy­wa­nia danych dostępowych, podob­nie jak w poprzed­niej częś­ci tej lekcji):

@RequestMapping(value = "/plik/{nazwa_pliku}", method = RequestMethod.GET)
public void getFile(@PathVariable("nazwa_pliku") String nazwaPliku, HttpServletResponse response) {
    try {
		AmazonS3 s3client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey));
		S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, key));
		InputStream is = object.getObjectContent();
		FileMetadata metedataObject = ...;//tutaj pobieramy dane z bazy danych
		response.setContentType(metadataObject.getContentType());
		response.setContentLength(metadataObject.getSize());
		org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
		response.flushBuffer();
    } catch (IOException ex) {
		log.info("Błąd przy pobieraniu pliku", ex);
		response.setStatus(404);
    }
}

Zwróć uwagę, jak przy­datne jest w tym wypad­ku posi­adanie metadanych pliku w bazie danych – dzię­ki temu może­my te infor­ma­c­je od razu uzu­pełnić i wysłać do użytkown­i­ka, w innym wypad­ku musielibyśmy je obliczać lub gen­erować w jak­iś sposób, co nie było­by takie proste.

Po wpisa­niu w przeglą­darkę adresu http://nasza.aplikacja.pl:8080/plik/przyklad.jpg powin­niśmy otrzy­mać nasz plik (oczy­wiś­cie pod warunk­iem, że go tam wcześniej umieś­cil­iśmy). Kod ten ma pod­sta­wową obsługę wyjatków – łapie wszys­tkie wyjąt­ki i zwraca wtedy kod http 404 oznacza­ją­cy, że plik nie ist­nieje. W rzeczy­wis­toś­ci każdy rodzaj wyjątku powinien być obsługi­wany odd­ziel­nie wraz z zapisy­waniem infor­ma­cji do loggera.

Tak obsługi­wany adres może­my wyko­rzys­tać zarówno jako link do pliku (tag HTML a) jak i jako źródło obraz­ka (w HTM­Lu <img src=”…” />

Dobre praktyki

W prak­tyce dostęp do plików, które nie są tajne (np. Zdję­cia pro­filowe, zdję­cia wgrane przez użytkown­i­ka itp) udostęp­nia się w inny sposób – poprzez tzw. CDN. CDN to skrót od Ang­iel­skiego wyraże­nia Con­tent Deliv­ery Net­work i jest to pode­jś­cie do udostęp­ni­a­nia treś­ci staty­cznych (jak zdję­cia) dużej licz­bie użytkown­ików (cza­sa­mi także w zależnoś­ci od regionu świa­ta, z którego pochodzą). Powo­dem takiego pode­jś­cia jest to, że ser­w­er aplikacji, aby odczy­tać taki plik a następ­nie odesłać go do użytkown­i­ka, musi wykon­ać sporo oper­acji. Jeśli mamy bard­zo dużo użytkown­ików, generu­je to real­ny, niemały koszt. Dlat­ego sto­su­je się ser­w­ery, które potrafią tylko i wyłącznie wyświ­et­lać treś­ci staty­czne – dzię­ki usunię­ciu wszys­t­kich innych funkcji są dużo szyb­sze od stan­dar­d­owych ser­w­erów, a więc koszt przez nie gen­erowany jest niższy.

W przy­pad­ku niek­tórych usług prze­chowywa­nia plików ist­nieją dedykowane rozwiąza­nia – np. Dla usłu­gi S3 ist­nieje usłu­ga Cloud­Front, która m.in. może dzi­ałać jako CDN dla plików prze­chowywanych w usłudze S3.

Tem­at ten nie będzie rozwinię­ty w tej lekcji ponieważ doty­czy on prob­lemów, na które napo­tykamy w przy­pad­ku więk­szych stron (z odsłon­a­mi lic­zony­mi w mil­ionach). Wtedy też najczęś­ciej zatrud­ni­amy po pros­tu osobę która ma w tym doświad­cze­nie zami­ast robić to samemu ;) Pon­ad­to dochodzi wtedy wiele kwestii szczegółowej kon­fig­u­racji, dos­tosowanej pod konkret­ną aplikację, aby maksy­mal­nie zwięk­szyć jej wydajność.

Podsumowanie

Ter­az wiesz już, jak możesz zarówno przesyłać pli­ki od użytkown­ików za pomocą API jak i umożli­wiać ich pobranie. Zachę­cam do przetestowa­nia tej metody z inny­mi pub­licznie dostęp­ny­mi API – np. Do pobiera­nia pogody (wheather.yahoo.com) czy też zwraca­ją­cy­mi współrzędne geograficzne adresu (Google Maps API). Możli­woś­ci są nieogranic­zone, a umiejęt­ność korzys­ta­nia z zewnętrznego API będzie Ci potrzeb­na nieraz.

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!