|
14 | 14 |
|
15 | 15 | #include "google/cloud/storage/internal/connection_impl.h" |
16 | 16 | #include "google/cloud/storage/internal/retry_object_read_source.h" |
| 17 | +#include "google/cloud/internal/filesystem.h" |
17 | 18 | #include "google/cloud/internal/opentelemetry.h" |
18 | 19 | #include "google/cloud/internal/rest_retry_loop.h" |
| 20 | +#include "google/cloud/log.h" |
19 | 21 | #include "absl/strings/match.h" |
20 | 22 | #include <chrono> |
| 23 | +#include <fstream> |
21 | 24 | #include <functional> |
22 | 25 | #include <memory> |
23 | 26 | #include <sstream> |
@@ -754,6 +757,103 @@ StatusOr<QueryResumableUploadResponse> StorageConnectionImpl::UploadChunk( |
754 | 757 | return RetryError(last_status, *retry_policy, __func__); |
755 | 758 | } |
756 | 759 |
|
| 760 | +StatusOr<std::unique_ptr<std::string>> StorageConnectionImpl::UploadFileSimple( |
| 761 | + std::string const& file_name, std::size_t file_size, |
| 762 | + InsertObjectMediaRequest& request) { |
| 763 | + auto upload_offset = request.GetOption<UploadFromOffset>().value_or(0); |
| 764 | + if (file_size < upload_offset) { |
| 765 | + std::ostringstream os; |
| 766 | + os << __func__ << "(" << request << ", " << file_name |
| 767 | + << "): UploadFromOffset (" << upload_offset |
| 768 | + << ") is bigger than the size of file source (" << file_size << ")"; |
| 769 | + return google::cloud::internal::InvalidArgumentError(std::move(os).str(), |
| 770 | + GCP_ERROR_INFO()); |
| 771 | + } |
| 772 | + auto upload_size = (std::min)( |
| 773 | + request.GetOption<UploadLimit>().value_or(file_size - upload_offset), |
| 774 | + file_size - upload_offset); |
| 775 | + |
| 776 | + std::ifstream is(file_name, std::ios::binary); |
| 777 | + if (!is.is_open()) { |
| 778 | + std::ostringstream os; |
| 779 | + os << __func__ << "(" << request << ", " << file_name |
| 780 | + << "): cannot open upload file source"; |
| 781 | + return google::cloud::internal::NotFoundError(std::move(os).str(), |
| 782 | + GCP_ERROR_INFO()); |
| 783 | + } |
| 784 | + |
| 785 | + auto payload = std::make_unique<std::string>( |
| 786 | + static_cast<std::size_t>(upload_size), char{}); |
| 787 | + is.seekg(upload_offset, std::ios::beg); |
| 788 | + // We need to use `&payload[0]` until C++17 |
| 789 | + // NOLINTNEXTLINE(readability-container-data-pointer) |
| 790 | + is.read(&(*payload)[0], payload->size()); |
| 791 | + if (static_cast<std::size_t>(is.gcount()) < payload->size()) { |
| 792 | + std::ostringstream os; |
| 793 | + os << __func__ << "(" << request << ", " << file_name << "): Actual read (" |
| 794 | + << is.gcount() << ") is smaller than upload_size (" << payload->size() |
| 795 | + << ")"; |
| 796 | + return google::cloud::internal::InternalError(std::move(os).str(), |
| 797 | + GCP_ERROR_INFO()); |
| 798 | + } |
| 799 | + is.close(); |
| 800 | + |
| 801 | + return payload; |
| 802 | +} |
| 803 | + |
| 804 | +StatusOr<std::unique_ptr<std::istream>> |
| 805 | +StorageConnectionImpl::UploadFileResumable(std::string const& file_name, |
| 806 | + ResumableUploadRequest& request) { |
| 807 | + auto upload_offset = request.GetOption<UploadFromOffset>().value_or(0); |
| 808 | + auto status = google::cloud::internal::status(file_name); |
| 809 | + if (!is_regular(status)) { |
| 810 | + GCP_LOG(WARNING) << "Trying to upload " << file_name |
| 811 | + << R"""( which is not a regular file. |
| 812 | +This is often a problem because: |
| 813 | + - Some non-regular files are infinite sources of data, and the load will |
| 814 | + never complete. |
| 815 | + - Some non-regular files can only be read once, and UploadFile() may need to |
| 816 | + read the file more than once to compute the checksum and hashes needed to |
| 817 | + preserve data integrity. |
| 818 | +
|
| 819 | +Consider using UploadLimit option or Client::WriteObject(). You may also need to disable data |
| 820 | +integrity checks using the DisableMD5Hash() and DisableCrc32cChecksum() options. |
| 821 | +)"""; |
| 822 | + } else { |
| 823 | + std::error_code size_err; |
| 824 | + auto file_size = google::cloud::internal::file_size(file_name, size_err); |
| 825 | + if (size_err) { |
| 826 | + return google::cloud::internal::NotFoundError(size_err.message(), |
| 827 | + GCP_ERROR_INFO()); |
| 828 | + } |
| 829 | + if (file_size < upload_offset) { |
| 830 | + std::ostringstream os; |
| 831 | + os << __func__ << "(" << request << ", " << file_name |
| 832 | + << "): UploadFromOffset (" << upload_offset |
| 833 | + << ") is bigger than the size of file source (" << file_size << ")"; |
| 834 | + return google::cloud::internal::InvalidArgumentError(std::move(os).str(), |
| 835 | + GCP_ERROR_INFO()); |
| 836 | + } |
| 837 | + |
| 838 | + auto upload_size = (std::min)( |
| 839 | + request.GetOption<UploadLimit>().value_or(file_size - upload_offset), |
| 840 | + file_size - upload_offset); |
| 841 | + request.set_option(UploadContentLength(upload_size)); |
| 842 | + } |
| 843 | + auto source = std::make_unique<std::ifstream>(file_name, std::ios::binary); |
| 844 | + if (!source->is_open()) { |
| 845 | + std::ostringstream os; |
| 846 | + os << __func__ << "(" << request << ", " << file_name |
| 847 | + << "): cannot open upload file source"; |
| 848 | + return google::cloud::internal::NotFoundError(std::move(os).str(), |
| 849 | + GCP_ERROR_INFO()); |
| 850 | + } |
| 851 | + // We set its offset before passing it to `UploadStreamResumable` so we don't |
| 852 | + // need to compute `UploadFromOffset` again. |
| 853 | + source->seekg(upload_offset, std::ios::beg); |
| 854 | + return std::unique_ptr<std::istream>(std::move(source)); |
| 855 | +} |
| 856 | + |
757 | 857 | StatusOr<ListBucketAclResponse> StorageConnectionImpl::ListBucketAcl( |
758 | 858 | ListBucketAclRequest const& request) { |
759 | 859 | auto const idempotency = current_idempotency_policy().IsIdempotent(request) |
|
0 commit comments