1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/base/upload_data.h" 6 7 #include "base/file_util.h" 8 #include "base/logging.h" 9 #include "base/string_util.h" 10 #include "net/base/file_stream.h" 11 #include "net/base/net_errors.h" 12 13 #ifdef ANDROID 14 #include "android/jni/platform_file_jni.h" 15 #endif 16 17 namespace net { 18 19 UploadData::Element::Element() 20 : type_(TYPE_BYTES), 21 file_range_offset_(0), 22 file_range_length_(kuint64max), 23 is_last_chunk_(false), 24 override_content_length_(false), 25 content_length_computed_(false), 26 content_length_(-1), 27 file_stream_(NULL) { 28 } 29 30 UploadData::Element::~Element() { 31 // In the common case |file__stream_| will be null. 32 delete file_stream_; 33 } 34 35 void UploadData::Element::SetToChunk(const char* bytes, 36 int bytes_len, 37 bool is_last_chunk) { 38 bytes_.clear(); 39 bytes_.insert(bytes_.end(), bytes, bytes + bytes_len); 40 type_ = TYPE_CHUNK; 41 is_last_chunk_ = is_last_chunk; 42 } 43 44 uint64 UploadData::Element::GetContentLength() { 45 if (override_content_length_ || content_length_computed_) 46 return content_length_; 47 48 if (type_ == TYPE_BYTES || type_ == TYPE_CHUNK) 49 return static_cast<uint64>(bytes_.size()); 50 else if (type_ == TYPE_BLOB) 51 // The blob reference will be resolved later. 52 return 0; 53 54 DCHECK_EQ(TYPE_FILE, type_); 55 DCHECK(!file_stream_); 56 57 // TODO(darin): This size calculation could be out of sync with the state of 58 // the file when we get around to reading it. We should probably find a way 59 // to lock the file or somehow protect against this error condition. 60 61 content_length_computed_ = true; 62 content_length_ = 0; 63 64 #ifdef ANDROID 65 if (file_path_.value().find("content://") == 0) { 66 content_length_computed_ = true; 67 content_length_ = android::contentUrlSize(file_path_); 68 return content_length_; 69 } 70 #endif 71 72 // We need to open the file here to decide if we should report the file's 73 // size or zero. We cache the open file, so that we can still read it when 74 // it comes time to. 75 file_stream_ = NewFileStreamForReading(); 76 if (!file_stream_) 77 return 0; 78 79 int64 length = 0; 80 if (!file_util::GetFileSize(file_path_, &length)) 81 return 0; 82 83 if (file_range_offset_ >= static_cast<uint64>(length)) 84 return 0; // range is beyond eof 85 86 // compensate for the offset and clip file_range_length_ to eof 87 content_length_ = std::min(length - file_range_offset_, file_range_length_); 88 return content_length_; 89 } 90 91 FileStream* UploadData::Element::NewFileStreamForReading() { 92 // In common usage GetContentLength() will call this first and store the 93 // result into |file_| and a subsequent call (from UploadDataStream) will 94 // get the cached open FileStream. 95 if (file_stream_) { 96 FileStream* file = file_stream_; 97 file_stream_ = NULL; 98 return file; 99 } 100 101 scoped_ptr<FileStream> file(new FileStream()); 102 int64 rv = file->Open(file_path_, 103 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); 104 if (rv != OK) { 105 // If the file can't be opened, we'll just upload an empty file. 106 DLOG(WARNING) << "Failed to open \"" << file_path_.value() 107 << "\" for reading: " << rv; 108 return NULL; 109 } 110 if (file_range_offset_) { 111 rv = file->Seek(FROM_BEGIN, file_range_offset_); 112 if (rv < 0) { 113 DLOG(WARNING) << "Failed to seek \"" << file_path_.value() 114 << "\" to offset: " << file_range_offset_ << " (" << rv 115 << ")"; 116 return NULL; 117 } 118 } 119 120 return file.release(); 121 } 122 123 UploadData::UploadData() 124 : identifier_(0), 125 chunk_callback_(NULL), 126 is_chunked_(false) { 127 } 128 129 void UploadData::AppendBytes(const char* bytes, int bytes_len) { 130 DCHECK(!is_chunked_); 131 if (bytes_len > 0) { 132 elements_.push_back(Element()); 133 elements_.back().SetToBytes(bytes, bytes_len); 134 } 135 } 136 137 void UploadData::AppendFile(const FilePath& file_path) { 138 DCHECK(!is_chunked_); 139 elements_.push_back(Element()); 140 elements_.back().SetToFilePath(file_path); 141 } 142 143 void UploadData::AppendFileRange(const FilePath& file_path, 144 uint64 offset, uint64 length, 145 const base::Time& expected_modification_time) { 146 DCHECK(!is_chunked_); 147 elements_.push_back(Element()); 148 elements_.back().SetToFilePathRange(file_path, offset, length, 149 expected_modification_time); 150 } 151 152 void UploadData::AppendBlob(const GURL& blob_url) { 153 DCHECK(!is_chunked_); 154 elements_.push_back(Element()); 155 elements_.back().SetToBlobUrl(blob_url); 156 } 157 158 void UploadData::AppendChunk(const char* bytes, 159 int bytes_len, 160 bool is_last_chunk) { 161 DCHECK(is_chunked_); 162 elements_.push_back(Element()); 163 elements_.back().SetToChunk(bytes, bytes_len, is_last_chunk); 164 if (chunk_callback_) 165 chunk_callback_->OnChunkAvailable(); 166 } 167 168 void UploadData::set_chunk_callback(ChunkCallback* callback) { 169 chunk_callback_ = callback; 170 } 171 172 uint64 UploadData::GetContentLength() { 173 uint64 len = 0; 174 std::vector<Element>::iterator it = elements_.begin(); 175 for (; it != elements_.end(); ++it) 176 len += (*it).GetContentLength(); 177 return len; 178 } 179 180 void UploadData::SetElements(const std::vector<Element>& elements) { 181 elements_ = elements; 182 } 183 184 UploadData::~UploadData() { 185 } 186 187 } // namespace net 188