Страницы

Поиск по вопросам

Показаны сообщения с ярлыком download. Показать все сообщения
Показаны сообщения с ярлыком download. Показать все сообщения

четверг, 13 февраля 2020 г.

Как удалить файл после скачивания его пользователем?

#php #download


Скачивание файла настроено через jquery, путем передачи ссылки на файл контроллером
и перехода по ней в jquery функцией location. Мне нужно удалить файл после того, как
он скачался.

Таким образом он выдается PHP в jquery

unlink(script_path() . "upload/temp/"  . $import_id . "/" . $sFileName);
echo str_replace('/index.php','/upload/temp/' . $import_id . "/" . $sFileName, site_url());


Если оставить unlink то файл удалиться перед тем как начнется скачивание. По сути
нужно что то типа ожидания в минуту после начала скачивания, а потом удалять файл (файл
не будет весить больше мегабайта).

Я думал сделать ожидание в жквери, но тогда пользователь может закрыть страницу и
скрипт прервется.
    


Ответы

Ответ 1



Складывайте такие файлы в отдельную папку на сервере. Напишите в crontab задачу N раз в день "удалять все файлы из данной папки, если они старше 24 часов", например. find /path/to/temp/files* -mtime +1 -exec rm {} \;

вторник, 10 декабря 2019 г.

Как определить скорость скачивания в многопоточной программе?

#c_sharp #download #benchmark


Был простой код скачивания:

internal static async Task DownloadFile(Uri uri)
{
  byte[] result;
  WebResponse response;
  var file = new ImageFile();
  var request = WebRequest.Create(uri);

  try
  {
    response = await request.GetResponseAsync();
    using (var ms = new MemoryStream())
    {
      response.GetResponseStream().CopyTo(ms);
      result = ms.ToArray();
    }
  }
  catch (System.Exception ex) { }
  if (response.ContentLength == result.LongLength)
    file.Body = result;
  return file;
}


Захотелось добавить показатель скорости скачивания. Гугл подсказал, что можно ориентироваться
на скорость чтения потока:

result = await CopyTo(response.GetResponseStream(), response.ContentLength, progressChanged);

private static async Task CopyTo(Stream from, long totalBytes, Action
loadedEvent)
{
  var sw = new Stopwatch();
  sw.Start();
  var data = new byte[totalBytes];
  byte[] buffer = new byte[81920];
  int currentIndex = 0;
  while (true)
  {
    int num = await from.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
    if (num != 0)
    {
      Array.Copy(buffer, 0, data, currentIndex, num);
      currentIndex += num;
      loadedEvent?.Invoke(new DownloadProgress(currentIndex, totalBytes, sw.ElapsedMilliseconds));
    }
    else
      break;
  }
  sw.Stop();
  return data;
}


Для хранения информации (и прокидывания наверх), завел простую структуру:

public struct DownloadProgress
{
  public readonly long BytesReceived;

  public readonly long TotalBytesToReceive;

  public readonly long TimeMs;

  public double GetSpeed()
  {
    var seconds = TimeMs / 1000.0;
    if (seconds > 0)
      return BytesReceived / seconds;
    return 0;
  }

  public DownloadProgress(long received, long total, long time)
  {
    this.BytesReceived = received;
    this.TotalBytesToReceive = total;
    this.TimeMs = time;
  }
}


Итого, если качать в один поток, то GetSpeed в любой момент времени (кроме первой
секунды где то) показывает реальную скорость.

Класс, который качает общую цифру в итоге хранит у себя:

    this.Speed = 0;
    var file = await ImageFile.DownloadFile(this.ImageLink, dp => this.Speed = dp.GetSpeed());
    this.Speed = 0;


Дальше я думал осталось самое легкое - просто на верхнем уровне сложил все скорости
и всё:

return this.ActivePages != null && this.ActivePages.Any() ? 
  this.ActivePages.Sum(p => p.Speed) : 0;


На деле, получилось очень неприятное поведение:


Большую часть времени скорость действительно отображается корректная.
Скорость часто скачет, причем разброс иногда превосходит ширину канала. При канале
в 650кб\сек цифры скачут от 300кб\сек до 5-8мб\сек.


Если скорость ниже вполне может быть при окончании одной загрузки и начале следующей,
то вот превышение ширины канала очевидно невозможно технически.
Возможно я где то округление пропустил, или Stopwatch недостаточно точен для таких
расчетов?

UPD: таки первое подозрение оправдалось - начальный скачок скорости портил общую
статистику. Если метод подсчета скорости сделать вот таким:

  public double GetSpeed()
  {
    var seconds = TimeMs / 1000.0;
    if (seconds > 0.1)
      return BytesReceived / seconds;
    return 0;
  }


То скорость уже в целом намного адекватнее, выше ширины канала изредка скачки ещё
бывают, но уже не больше 5%. Можно увеличить игнорируемое время, тогда скачков не будет
совсем.

Осталась проблема с общим показателем. Если для одного потока цифра была достоверной,
то с многопоточным скачиванием цифра часто врёт, средняя от показателя получается ниже
реальной (общее время на общий объем).

UPD2: минимизировал все расчеты, убрал структуру, вынес логику в статический класс:

  public class NetworkSpeed
  {
    public static double TotalSpeed { get { return totalSpeed; } }

    private static double totalSpeed = 0;

    private const uint Seconds = 3;

    private const uint TimerInterval = 1000;

    private static Timer speedTimer = new Timer(state =>
    {
      var now = 0L;
      while (receivedStorage.Value.Any())
      {
        long added;
        if (receivedStorage.Value.TryDequeue(out added))
        {
          now += added;
        }
      }
      lastSpeeds.Value.Enqueue(now);
      totalSpeed = lastSpeeds.Value.Average();
    }, null, 0, TimerInterval);

    private static Lazy> lastSpeeds = new Lazy>(()
=> new LimitedConcurrentQueue(Seconds));

    private static Lazy> receivedStorage = new Lazy>();

    public static void Clear()
    {
      while (receivedStorage.Value.Count > 0)
      {
        long dd;
        receivedStorage.Value.TryDequeue(out dd);
      }
      while (lastSpeeds.Value.Count > 0)
      {
        double dd;
        lastSpeeds.Value.TryDequeue(out dd);
      }
    }

    public static void AddInfo(long received)
    {
      receivedStorage.Value.Enqueue(received);
    }

    private class LimitedConcurrentQueue : ConcurrentQueue
    {
      public uint Limit { get; }

      public new void Enqueue(T item)
      {
        while (Count >= Limit)
        {
          T deleted;
          TryDequeue(out deleted);
        }
        base.Enqueue(item);
      }

      public LimitedConcurrentQueue(uint limit)
      {
        Limit = limit;
      }
    }
  }


В итоге, при скачивании достаточно сообщать, сколько байт было скачано в очередной
момент:

      NetworkSpeed.AddInfo(num);


И всё, показатель NetworkSpeed.TotalSpeed будет отображать среднюю за последние 3
секунды скорость. Средний показатель в целом стал более-менее стабильным, правда немного
завышает показатели на моих данных. Ну и очевидно, что если тредпул будет перегружен,
то таймер вовремя не отработает и скорость начнёт "скакать".
    


Ответы

Ответ 1



Публикую решение, которое меня устроило. Точность итоговой цифры 95-99%. public class NetworkSpeed { public static double TotalSpeed { get { return totalSpeed; } } private static double totalSpeed = 0; private const uint Seconds = 3; private const uint TimerInterval = 1000; private static Timer speedTimer = new Timer(state => { var now = 0L; while (receivedStorage.Value.Any()) { long added; if (receivedStorage.Value.TryDequeue(out added)) { now += added; } } lastSpeeds.Value.Enqueue(now); totalSpeed = lastSpeeds.Value.Average(); }, null, 0, TimerInterval); private static Lazy> lastSpeeds = new Lazy>(() => new LimitedConcurrentQueue(Seconds)); private static Lazy> receivedStorage = new Lazy>(); public static void Clear() { while (receivedStorage.Value.Count > 0) { long dd; receivedStorage.Value.TryDequeue(out dd); } while (lastSpeeds.Value.Count > 0) { double dd; lastSpeeds.Value.TryDequeue(out dd); } } public static void AddInfo(long received) { receivedStorage.Value.Enqueue(received); } private class LimitedConcurrentQueue : ConcurrentQueue { public uint Limit { get; } public new void Enqueue(T item) { while (Count >= Limit) { T deleted; TryDequeue(out deleted); } base.Enqueue(item); } public LimitedConcurrentQueue(uint limit) { Limit = limit; } } } Как этим пользоваться - на верхнем уровне вызываем Clear() когда начинаем или заканчиваем скачивание, чтобы результаты были независимы от других загрузок. В месте, где происходит реальное скачивание - вызываем метод AddInfo, указав сколько байт нам пришло в очередной цикл скачивания. Можно использовать CopyTo из шапки или DownloadProgressChanged у WebClient. Главное передавать именно разницу между предыдущим показателем и текущим. Точность измерений обеспечивается таймером (System.Threading.Timer), а потому, чтобы точность показаний была достоверной, тредпул должен быть свободен для вызова callback. Ну и, понятное дело, результат всех измерений находится в свойстве TotalSpeed. Если хочется, можно добавить событие о его изменении, для своевременного отображения в UI. Частота его обновления специально синхронизирована с таймером - иначе цифра меняется слишком часто и пользователь не понимает, какова скорость.

четверг, 5 декабря 2019 г.

Как скопировать mp3 файл из Интернетов?

#java #mp3 #download


Вопрос очень простой. Мой код копирует, но при воспроизведении какие-то прерывания
идут. Что я сделал не так?

        try {
        URL url = new URL("http://cdn.echo.msk.ru/snd/2017-07-28-osoboe-1908.mp3");
        BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c:/ww.mp3")));
        int c=0;
        while ((c=br.read())!=-1)
            bw.write(c);
        br.close();
        bw.close();
    } catch (MalformedURLException e){}
    catch (IOException e){}
    System.out.println("End");

    


Ответы

Ответ 1



таким образом проще сделать: URL website = new URL("http://cdn.echo.msk.ru/snd/2017-07-28-osoboe-1908.mp3"); Path target = Paths.get("file.mp3"); try (InputStream in = website.openStream()) { Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING); }

вторник, 16 июля 2019 г.

Скачивание файла с помощью X-Accel-Redirect

NGINX пишет not found, когда я пытаюсь скачать файл с сайта http://site.com/download/?id=95
папка /_files лежит на уровень выше /www, т.е.
тут скрипты: /site/www/
а тут файлы: /site/_files/
В папке /_files могут быть подпапки.
Конфиг сайта
server { listen *:80; ## listen for ipv4 server_name site.com; access_log /var/log/nginx/access-site.log; location / { proxy_pass http://127.0.0.1:88/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_connect_timeout 120; proxy_send_timeout 120; proxy_read_timeout 180; }
location /download/ { internal; alias /var/sites/site/_files/; }
location ~* \.(jpg|jpeg|gif|png|webp|ico|css|bmp|swf|js|html|txt)$ { root /var/sites/site/www; }
}
Скрипт который скачивает файл
header("X-Accel-Redirect: /download/".$route); header("Content-Type: application/x-force-download"); header("Content-Disposition: attachment; filename=\"".$file."\"");
В итоге заголовок выглядит так
X-Accel-Redirect: download/content/95/arch.rar
В чем может быть проблема?


Ответ

проблема оказалась в попытке использования внутреннего (internal) location-а /download в качестве внешнего (для ссылок из внешнего мира вида http://site.tld/download/...).
внутренним этот location является по требованиям самого nginx-а для корректной работы механизма X-Accel
решается проблема переименованием location-а в какое-нибудь точно неиспользуемое имя. например:
location /intenal-download { internal; alias /путь/к/файлам; }
и одновременно надо возвращать соответствующее значение в заголовке X-Accel-Redirect. основываясь на приведённом примере, примерно так:
header("X-Accel-Redirect: /internal-download/".$route);
обратите внимание, что путь в заголовке должен начинаться со слэша.

четверг, 11 июля 2019 г.

Java download file

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


Ответ

На самом деле, понятие расширения пришло из Windows. В Linux название файлов включает и то, что находится после точки тоже. Получить названия файла можно, если сервер его включил в Content-Disposition. Ну, по крайней мере, стандарт HTTP говорит, что эта информация находится именно там. Content-Disposition можно взять так:
String uri = "http://www.website.com/download/file.pdf"; URL url = new URL(uri); URLConnection conn = url.openConnection(); Map header =conn.getHeaderFields(); header.get("Content-Disposition")
Проблема в том, что не все это делают. Например Google Drive на команду download в Content-Disposition возвращает json.txt, в котором есть инструкции по файлу. Выглядит это примерно так:
disposition: "SCAN_CLEAN" downloadUrl: "https://doc-10-bc-docs.googleusercontent.com/docs/securesc/xxxxxxxxxxxx/xxxxxxxxxxxx/xxxxxxxxxxxx/xxxxxxxxxxxx/xxxxxxxxxxxx/xxxxxxxxxxxx?e=download" fileName: "Oxford_dictionary.zip" scanResult: "OK" sizeBytes: 431943
Есть в header ещё Content-Type. Из него можно выудить отдельно расширение (точнее медиа-тип) файла. Брать нужно как-то так:
String uri = "http://www.website.com/download/file.pdf"; URL url = new URL(uri); URLConnection conn = url.openConnection(); String type = conn.getContentType();
Возвращает он строку типа:
application/pdf
Но чего-то стандартного, насколько я знаю, нет.

понедельник, 10 июня 2019 г.

Ни с одного браузера не скачивается файл

Знаю, это напрямую не связано с программированием, тем не менее, проблема есть. https://2appstudio.com/journey/ Вот с этого сайта не могу скачать ни 32-, ни 64-битную версию для Windows. Тоже самое и в Firefox, примерно. Помогите, пожалуйста. Программа уж больно понравилась. Спасибо заранее.


Ответ

Настроить хром так, чтобы он не удалял подозрительные файлы самостоятельно: https://superuser.com/questions/387724/how-to-disable-download-scanning-protection-of-new-chrome-17 Выбрать сохранение скачанного файла в следующем запросе:

понедельник, 29 октября 2018 г.

Как определить скорость скачивания в многопоточной программе?

Был простой код скачивания:
internal static async Task DownloadFile(Uri uri) { byte[] result; WebResponse response; var file = new ImageFile(); var request = WebRequest.Create(uri);
try { response = await request.GetResponseAsync(); using (var ms = new MemoryStream()) { response.GetResponseStream().CopyTo(ms); result = ms.ToArray(); } } catch (System.Exception ex) { } if (response.ContentLength == result.LongLength) file.Body = result; return file; }
Захотелось добавить показатель скорости скачивания. Гугл подсказал, что можно ориентироваться на скорость чтения потока:
result = await CopyTo(response.GetResponseStream(), response.ContentLength, progressChanged);
private static async Task CopyTo(Stream from, long totalBytes, Action loadedEvent) { var sw = new Stopwatch(); sw.Start(); var data = new byte[totalBytes]; byte[] buffer = new byte[81920]; int currentIndex = 0; while (true) { int num = await from.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); if (num != 0) { Array.Copy(buffer, 0, data, currentIndex, num); currentIndex += num; loadedEvent?.Invoke(new DownloadProgress(currentIndex, totalBytes, sw.ElapsedMilliseconds)); } else break; } sw.Stop(); return data; }
Для хранения информации (и прокидывания наверх), завел простую структуру:
public struct DownloadProgress { public readonly long BytesReceived;
public readonly long TotalBytesToReceive;
public readonly long TimeMs;
public double GetSpeed() { var seconds = TimeMs / 1000.0; if (seconds > 0) return BytesReceived / seconds; return 0; }
public DownloadProgress(long received, long total, long time) { this.BytesReceived = received; this.TotalBytesToReceive = total; this.TimeMs = time; } }
Итого, если качать в один поток, то GetSpeed в любой момент времени (кроме первой секунды где то) показывает реальную скорость.
Класс, который качает общую цифру в итоге хранит у себя:
this.Speed = 0; var file = await ImageFile.DownloadFile(this.ImageLink, dp => this.Speed = dp.GetSpeed()); this.Speed = 0;
Дальше я думал осталось самое легкое - просто на верхнем уровне сложил все скорости и всё:
return this.ActivePages != null && this.ActivePages.Any() ? this.ActivePages.Sum(p => p.Speed) : 0;
На деле, получилось очень неприятное поведение:
Большую часть времени скорость действительно отображается корректная. Скорость часто скачет, причем разброс иногда превосходит ширину канала. При канале в 650кб\сек цифры скачут от 300кб\сек до 5-8мб\сек.
Если скорость ниже вполне может быть при окончании одной загрузки и начале следующей, то вот превышение ширины канала очевидно невозможно технически. Возможно я где то округление пропустил, или Stopwatch недостаточно точен для таких расчетов?
UPD: таки первое подозрение оправдалось - начальный скачок скорости портил общую статистику. Если метод подсчета скорости сделать вот таким:
public double GetSpeed() { var seconds = TimeMs / 1000.0; if (seconds > 0.1) return BytesReceived / seconds; return 0; }
То скорость уже в целом намного адекватнее, выше ширины канала изредка скачки ещё бывают, но уже не больше 5%. Можно увеличить игнорируемое время, тогда скачков не будет совсем.
Осталась проблема с общим показателем. Если для одного потока цифра была достоверной, то с многопоточным скачиванием цифра часто врёт, средняя от показателя получается ниже реальной (общее время на общий объем).
UPD2: минимизировал все расчеты, убрал структуру, вынес логику в статический класс:
public class NetworkSpeed { public static double TotalSpeed { get { return totalSpeed; } }
private static double totalSpeed = 0;
private const uint Seconds = 3;
private const uint TimerInterval = 1000;
private static Timer speedTimer = new Timer(state => { var now = 0L; while (receivedStorage.Value.Any()) { long added; if (receivedStorage.Value.TryDequeue(out added)) { now += added; } } lastSpeeds.Value.Enqueue(now); totalSpeed = lastSpeeds.Value.Average(); }, null, 0, TimerInterval);
private static Lazy> lastSpeeds = new Lazy>(() => new LimitedConcurrentQueue(Seconds));
private static Lazy> receivedStorage = new Lazy>();
public static void Clear() { while (receivedStorage.Value.Count > 0) { long dd; receivedStorage.Value.TryDequeue(out dd); } while (lastSpeeds.Value.Count > 0) { double dd; lastSpeeds.Value.TryDequeue(out dd); } }
public static void AddInfo(long received) { receivedStorage.Value.Enqueue(received); }
private class LimitedConcurrentQueue : ConcurrentQueue { public uint Limit { get; }
public new void Enqueue(T item) { while (Count >= Limit) { T deleted; TryDequeue(out deleted); } base.Enqueue(item); }
public LimitedConcurrentQueue(uint limit) { Limit = limit; } } }
В итоге, при скачивании достаточно сообщать, сколько байт было скачано в очередной момент:
NetworkSpeed.AddInfo(num);
И всё, показатель NetworkSpeed.TotalSpeed будет отображать среднюю за последние 3 секунды скорость. Средний показатель в целом стал более-менее стабильным, правда немного завышает показатели на моих данных. Ну и очевидно, что если тредпул будет перегружен, то таймер вовремя не отработает и скорость начнёт "скакать".


Ответ

Публикую решение, которое меня устроило. Точность итоговой цифры 95-99%.
public class NetworkSpeed { public static double TotalSpeed { get { return totalSpeed; } }
private static double totalSpeed = 0;
private const uint Seconds = 3;
private const uint TimerInterval = 1000;
private static Timer speedTimer = new Timer(state => { var now = 0L; while (receivedStorage.Value.Any()) { long added; if (receivedStorage.Value.TryDequeue(out added)) { now += added; } } lastSpeeds.Value.Enqueue(now); totalSpeed = lastSpeeds.Value.Average(); }, null, 0, TimerInterval);
private static Lazy> lastSpeeds = new Lazy>(() => new LimitedConcurrentQueue(Seconds));
private static Lazy> receivedStorage = new Lazy>();
public static void Clear() { while (receivedStorage.Value.Count > 0) { long dd; receivedStorage.Value.TryDequeue(out dd); } while (lastSpeeds.Value.Count > 0) { double dd; lastSpeeds.Value.TryDequeue(out dd); } }
public static void AddInfo(long received) { receivedStorage.Value.Enqueue(received); }
private class LimitedConcurrentQueue : ConcurrentQueue { public uint Limit { get; }
public new void Enqueue(T item) { while (Count >= Limit) { T deleted; TryDequeue(out deleted); } base.Enqueue(item); }
public LimitedConcurrentQueue(uint limit) { Limit = limit; } } }
Как этим пользоваться - на верхнем уровне вызываем Clear() когда начинаем или заканчиваем скачивание, чтобы результаты были независимы от других загрузок. В месте, где происходит реальное скачивание - вызываем метод AddInfo, указав сколько байт нам пришло в очередной цикл скачивания. Можно использовать CopyTo из шапки или DownloadProgressChanged у WebClient. Главное передавать именно разницу между предыдущим показателем и текущим.
Точность измерений обеспечивается таймером (System.Threading.Timer), а потому, чтобы точность показаний была достоверной, тредпул должен быть свободен для вызова callback
Ну и, понятное дело, результат всех измерений находится в свойстве TotalSpeed. Если хочется, можно добавить событие о его изменении, для своевременного отображения в UI. Частота его обновления специально синхронизирована с таймером - иначе цифра меняется слишком часто и пользователь не понимает, какова скорость.

вторник, 9 октября 2018 г.

Как скопировать mp3 файл из Интернетов?

Вопрос очень простой. Мой код копирует, но при воспроизведении какие-то прерывания идут. Что я сделал не так?
try { URL url = new URL("http://cdn.echo.msk.ru/snd/2017-07-28-osoboe-1908.mp3"); BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c:/ww.mp3"))); int c=0; while ((c=br.read())!=-1) bw.write(c); br.close(); bw.close(); } catch (MalformedURLException e){} catch (IOException e){} System.out.println("End");


Ответ

таким образом проще сделать:
URL website = new URL("http://cdn.echo.msk.ru/snd/2017-07-28-osoboe-1908.mp3"); Path target = Paths.get("file.mp3"); try (InputStream in = website.openStream()) { Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING); }