// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/upload_data.h" #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" #include "net/base/file_stream.h" #include "net/base/net_errors.h" #ifdef ANDROID #include "android/jni/platform_file_jni.h" #endif namespace net { UploadData::Element::Element() : type_(TYPE_BYTES), file_range_offset_(0), file_range_length_(kuint64max), is_last_chunk_(false), override_content_length_(false), content_length_computed_(false), content_length_(-1), file_stream_(NULL) { } UploadData::Element::~Element() { // In the common case |file__stream_| will be null. delete file_stream_; } void UploadData::Element::SetToChunk(const char* bytes, int bytes_len, bool is_last_chunk) { bytes_.clear(); bytes_.insert(bytes_.end(), bytes, bytes + bytes_len); type_ = TYPE_CHUNK; is_last_chunk_ = is_last_chunk; } uint64 UploadData::Element::GetContentLength() { if (override_content_length_ || content_length_computed_) return content_length_; if (type_ == TYPE_BYTES || type_ == TYPE_CHUNK) return static_cast<uint64>(bytes_.size()); else if (type_ == TYPE_BLOB) // The blob reference will be resolved later. return 0; DCHECK_EQ(TYPE_FILE, type_); DCHECK(!file_stream_); // TODO(darin): This size calculation could be out of sync with the state of // the file when we get around to reading it. We should probably find a way // to lock the file or somehow protect against this error condition. content_length_computed_ = true; content_length_ = 0; #ifdef ANDROID if (file_path_.value().find("content://") == 0) { content_length_computed_ = true; content_length_ = android::contentUrlSize(file_path_); return content_length_; } #endif // We need to open the file here to decide if we should report the file's // size or zero. We cache the open file, so that we can still read it when // it comes time to. file_stream_ = NewFileStreamForReading(); if (!file_stream_) return 0; int64 length = 0; if (!file_util::GetFileSize(file_path_, &length)) return 0; if (file_range_offset_ >= static_cast<uint64>(length)) return 0; // range is beyond eof // compensate for the offset and clip file_range_length_ to eof content_length_ = std::min(length - file_range_offset_, file_range_length_); return content_length_; } FileStream* UploadData::Element::NewFileStreamForReading() { // In common usage GetContentLength() will call this first and store the // result into |file_| and a subsequent call (from UploadDataStream) will // get the cached open FileStream. if (file_stream_) { FileStream* file = file_stream_; file_stream_ = NULL; return file; } scoped_ptr<FileStream> file(new FileStream()); int64 rv = file->Open(file_path_, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); if (rv != OK) { // If the file can't be opened, we'll just upload an empty file. DLOG(WARNING) << "Failed to open \"" << file_path_.value() << "\" for reading: " << rv; return NULL; } if (file_range_offset_) { rv = file->Seek(FROM_BEGIN, file_range_offset_); if (rv < 0) { DLOG(WARNING) << "Failed to seek \"" << file_path_.value() << "\" to offset: " << file_range_offset_ << " (" << rv << ")"; return NULL; } } return file.release(); } UploadData::UploadData() : identifier_(0), chunk_callback_(NULL), is_chunked_(false) { } void UploadData::AppendBytes(const char* bytes, int bytes_len) { DCHECK(!is_chunked_); if (bytes_len > 0) { elements_.push_back(Element()); elements_.back().SetToBytes(bytes, bytes_len); } } void UploadData::AppendFile(const FilePath& file_path) { DCHECK(!is_chunked_); elements_.push_back(Element()); elements_.back().SetToFilePath(file_path); } void UploadData::AppendFileRange(const FilePath& file_path, uint64 offset, uint64 length, const base::Time& expected_modification_time) { DCHECK(!is_chunked_); elements_.push_back(Element()); elements_.back().SetToFilePathRange(file_path, offset, length, expected_modification_time); } void UploadData::AppendBlob(const GURL& blob_url) { DCHECK(!is_chunked_); elements_.push_back(Element()); elements_.back().SetToBlobUrl(blob_url); } void UploadData::AppendChunk(const char* bytes, int bytes_len, bool is_last_chunk) { DCHECK(is_chunked_); elements_.push_back(Element()); elements_.back().SetToChunk(bytes, bytes_len, is_last_chunk); if (chunk_callback_) chunk_callback_->OnChunkAvailable(); } void UploadData::set_chunk_callback(ChunkCallback* callback) { chunk_callback_ = callback; } uint64 UploadData::GetContentLength() { uint64 len = 0; std::vector<Element>::iterator it = elements_.begin(); for (; it != elements_.end(); ++it) len += (*it).GetContentLength(); return len; } void UploadData::SetElements(const std::vector<Element>& elements) { elements_ = elements; } UploadData::~UploadData() { } } // namespace net