Java получает имя файла загрузки с перенаправленного «дружественного» URL-адреса

Я пытаюсь загрузить файл с заданного URL-адреса, который может быть или не быть прямой ссылкой на файл. Кто-нибудь знает, как я могу определить имя файла для записи, если URL-адрес является косвенной ссылкой (т.е. http://www.example.com/download.php?getFile=1) ? Это не проблема, если URL-адрес является прямой ссылкой для извлечения имени файла из URL-адреса и начала записи в извлеченное имя файла, но с ссылкой перенаправления единственный метод, который я нашел до сих пор, - это запись в произвольное имя файла - foo.txt - а потом попробуй с этим поработать. Проблема в том, что мне действительно нужно правильное имя файла (и расширение). Пример кода, который я использую: (раздел в предложении else не закончен и не работает):

public static boolean dlFile(String URL, String dest){
    try{
        URL grab = new URL(URL);
        ReadableByteChannel rbc = Channels.newChannel(grab.openStream());
        String fnRE = ".*/([a-zA-Z0-9\\-\\._]+)$";
            Pattern pattern = Pattern.compile(fnRE);
        Matcher matcher = pattern.matcher(URL);
        String fName = "";
        if(matcher.find()) fName = matcher.group(1);
        else { //filename cannot be extracted - do something here - below doesn't work raises MalformedURLExcpetion
            URL foo = new URL(URL);
            HttpURLConnection fooConnection = (HttpURLConnection) foo.openConnection();
            URL secondFoo = new URL(fooConnection.getHeaderField("Location"));
            System.out.println("Redirect URL: "+secondFoo);
            fooConnection.setInstanceFollowRedirects(false);
            URLConnection fooURL = secondFoo.openConnection();
        }
        System.out.println("Connection to "+URL+" established!");
        if(dest.endsWith("/")){}
        else dest+="/";
        System.out.println("Writing "+fName+" to "+dest);
        FileOutputStream fos = new FileOutputStream(dest+fName);
        fos.getChannel().transferFrom(rbc, 0, 1 << 24);

Я уверен, что должен быть простой способ получить имя файла из заголовков или что-то в этом роде, но я не могу понять, как его получить. Заранее спасибо,


person psf    schedule 02.11.2012    source источник
comment
В общем, нет, хотя, если ответ имеет заголовок Content-Disposition: attachment; filename=myfile.zip, вы можете извлечь из него имя файла.   -  person Ian Roberts    schedule 02.11.2012
comment
@IanRoberts - да, я так и думал, но, к сожалению, заголовок Content-Disposition не возвращается, все заголовки, которые я могу получить, перейдя по URL-адресу, просто сообщают мне его php/html и т. д.   -  person psf    schedule 02.11.2012


Ответы (3)


Предполагая, что в ответе есть поле заголовка «Местоположение», я смог получить прямую ссылку на URL-адрес, содержащий несколько перенаправлений, например:

String location = "http://www.example.com/download.php?getFile=1";
HttpURLConnection connection = null;
for (;;) {
    URL url = new URL(location);
    connection = (HttpURLConnection) url.openConnection();
    connection.setInstanceFollowRedirects(false);
    String redirectLocation = connection.getHeaderField("Location");
    if (redirectLocation == null) break;
    location = redirectLocation;
}
//and finally:
String fileName = location.substring(location.lastIndexOf('/') + 1, location.length());
person tingo    schedule 02.03.2016

Я думаю, что лучше использовать библиотеку Java Jsoup, а затем использовать приведенную ниже метод:

public static void downloadFileJsoup(String URL, String PATH) throws IOException {
    Response res = Jsoup.connect(URL)
            .userAgent("Mozilla")
            .timeout(30000)
            .followRedirects(true)
            .ignoreContentType(true)
            .maxBodySize(20000000)//Increase value if download is more than 20MB
            .execute(); 
    String remoteFilename=res.header("Content-Disposition").replaceFirst("(?i)^.*filename=\"?([^\"]+)\"?.*$", "$1");
    String filename = PATH + remoteFilename;
    FileOutputStream out = (new FileOutputStream(new java.io.File(filename)));
    out.write( res.bodyAsBytes());
    out.close();
}
person Rodrigo Eggea    schedule 03.11.2020

Нет, в общем никак. Обычно ответ не содержит этой информации, поскольку вы не добавляете никакой информации о собственном протоколе в поток данных (на случай, если вы можете управлять сервером).

В любом случае, вы просите расширение имени файла. Возможно, с правильным типом контента все готово.

person PeterMmm    schedule 02.11.2012
comment
нет ли способа загрузить файл, не зная имени файла для записи? нет ли способа сказать «загрузить с именем файла по умолчанию» или что-то в этом роде? это кажется безумием, так как это легко выполнимо на других языках, но мне нужна эта работа в java. бу :-( - person psf; 02.11.2012
comment
Нет, то есть HTTP работает и к Java отношения не имеет. Если сервер не предоставит вам эту информацию (Content-Disposition), вы можете прочитать байты в буфере. Некоторые форматы файлов содержат собственное оригинальное имя файла (особенно открытые форматы). - person PeterMmm; 02.11.2012
comment
Привет, да, ты прав. Я только что посмотрел на скрипт Python, который я написал, чтобы сделать то же самое, и он работает только при чтении Content-Disposition из заголовка. Я предполагаю, что когда я подключаю URL-адрес к urllib2, он должен следовать перенаправлению, прежде чем возвращать мне заголовки, поскольку он дает совершенно разные заголовки для того, что я могу получить от java. Я уверен, что читал, что вы можете установить свойство для принудительного перенаправления, но я не могу заставить его работать. Хмммммм. - person psf; 02.11.2012
comment
@psf В отсутствие заголовка Content-Disposition браузеры и инструменты, такие как wget, просто угадывают имя файла, которое нужно использовать, из последнего сегмента пути URL-адреса. - person Ian Roberts; 02.11.2012