export_output_file.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "export/output/export_output_file.h"
  8. #include "export/output/export_output_result.h"
  9. #include "export/output/export_output_stats.h"
  10. #include "base/qt/qt_string_view.h"
  11. #include <QtCore/QFileInfo>
  12. #include <QtCore/QDir>
  13. #include <gsl/util>
  14. namespace Export {
  15. namespace Output {
  16. File::File(const QString &path, Stats *stats) : _path(path), _stats(stats) {
  17. }
  18. int64 File::size() const {
  19. return _offset;
  20. }
  21. bool File::empty() const {
  22. return !_offset;
  23. }
  24. Result File::writeBlock(const QByteArray &block) {
  25. const auto result = writeBlockAttempt(block);
  26. if (!result) {
  27. _file.reset();
  28. }
  29. return result;
  30. }
  31. Result File::writeBlockAttempt(const QByteArray &block) {
  32. if (_stats && !_inStats) {
  33. _inStats = true;
  34. _stats->incrementFiles();
  35. }
  36. if (const auto result = reopen(); !result) {
  37. return result;
  38. }
  39. const auto size = block.size();
  40. if (!size) {
  41. return Result::Success();
  42. }
  43. if (_file->write(block) == size && _file->flush()) {
  44. _offset += size;
  45. if (_stats) {
  46. _stats->incrementBytes(size);
  47. }
  48. return Result::Success();
  49. }
  50. return error();
  51. }
  52. Result File::reopen() {
  53. if (_file && _file->isOpen()) {
  54. return Result::Success();
  55. }
  56. _file.emplace(_path);
  57. if (_file->exists()) {
  58. if (_file->size() < _offset) {
  59. return fatalError();
  60. } else if (!_file->resize(_offset)) {
  61. return error();
  62. }
  63. } else if (_offset > 0) {
  64. return fatalError();
  65. }
  66. if (_file->open(QIODevice::Append)) {
  67. return Result::Success();
  68. }
  69. const auto info = QFileInfo(_path);
  70. const auto dir = info.absoluteDir();
  71. return (!dir.exists()
  72. && dir.mkpath(dir.absolutePath())
  73. && _file->open(QIODevice::Append))
  74. ? Result::Success()
  75. : error();
  76. }
  77. Result File::error() const {
  78. return Result(Result::Type::Error, _path);
  79. }
  80. Result File::fatalError() const {
  81. return Result(Result::Type::FatalError, _path);
  82. }
  83. QString File::PrepareRelativePath(
  84. const QString &folder,
  85. const QString &suggested) {
  86. if (!QFile::exists(folder + suggested)) {
  87. return suggested;
  88. }
  89. // Not lastIndexOf('.') so that "file.tar.xz" won't be messed up.
  90. const auto position = suggested.indexOf('.');
  91. const auto base = suggested.mid(0, position);
  92. const auto extension = (position >= 0)
  93. ? base::StringViewMid(suggested, position)
  94. : QStringView();
  95. const auto relativePart = [&](int attempt) {
  96. auto result = base + QString(" (%1)").arg(attempt);
  97. result.append(extension);
  98. return result;
  99. };
  100. auto attempt = 0;
  101. while (true) {
  102. const auto relativePath = relativePart(++attempt);
  103. if (!QFile::exists(folder + relativePath)) {
  104. return relativePath;
  105. }
  106. }
  107. }
  108. Result File::Copy(
  109. const QString &source,
  110. const QString &path,
  111. Stats *stats) {
  112. QFile f(source);
  113. if (!f.exists() || !f.open(QIODevice::ReadOnly)) {
  114. return Result(Result::Type::FatalError, source);
  115. }
  116. const auto bytes = f.readAll();
  117. if (bytes.size() != f.size()) {
  118. return Result(Result::Type::FatalError, source);
  119. }
  120. return File(path, stats).writeBlock(bytes);
  121. }
  122. } // namespace Output
  123. } // namespace File