You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
559 lines
19 KiB
559 lines
19 KiB
/* Copyright 2010-2011 10gen Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
using MongoDB.Bson;
|
|
using MongoDB.Bson.IO;
|
|
using MongoDB.Bson.Serialization;
|
|
using MongoDB.Driver.Builders;
|
|
|
|
namespace MongoDB.Driver.GridFS {
|
|
/// <summary>
|
|
/// Represents information about a GridFS file (patterned after .NET's FileInfo class).
|
|
/// </summary>
|
|
public class MongoGridFSFileInfo : IBsonSerializable {
|
|
#region private fields
|
|
// these fields are considered in Equals and GetHashCode
|
|
private string[] aliases;
|
|
private int chunkSize;
|
|
private string contentType;
|
|
private BsonValue id; // usually a BsonObjectId but not required to be
|
|
private int length;
|
|
private string md5;
|
|
private BsonDocument metadata;
|
|
private string name;
|
|
private DateTime uploadDate;
|
|
|
|
// these fields are not considered in Equals and GetHashCode
|
|
private bool cached; // true if info came from database
|
|
private bool exists;
|
|
private MongoGridFS gridFS;
|
|
#endregion
|
|
|
|
#region constructors
|
|
// used by Deserialize
|
|
private MongoGridFSFileInfo() {
|
|
}
|
|
|
|
internal MongoGridFSFileInfo(
|
|
MongoGridFS gridFS,
|
|
BsonDocument fileInfo
|
|
) {
|
|
this.gridFS = gridFS;
|
|
CacheFileInfo(fileInfo);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the GridFSFileInfo class.
|
|
/// </summary>
|
|
/// <param name="gridFS">The GridFS file system that contains the GridFS file.</param>
|
|
/// <param name="remoteFileName">The remote file name.</param>
|
|
public MongoGridFSFileInfo(
|
|
MongoGridFS gridFS,
|
|
string remoteFileName
|
|
)
|
|
: this(gridFS, remoteFileName, gridFS.Settings.ChunkSize) {
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the GridFSFileInfo class.
|
|
/// </summary>
|
|
/// <param name="gridFS">The GridFS file system that contains the GridFS file.</param>
|
|
/// <param name="remoteFileName">The remote file name.</param>
|
|
/// <param name="chunkSize">The chunk size.</param>
|
|
public MongoGridFSFileInfo(
|
|
MongoGridFS gridFS,
|
|
string remoteFileName,
|
|
int chunkSize
|
|
) {
|
|
this.gridFS = gridFS;
|
|
this.chunkSize = chunkSize;
|
|
this.name = remoteFileName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the GridFSFileInfo class.
|
|
/// </summary>
|
|
/// <param name="gridFS">The GridFS file system that contains the GridFS file.</param>
|
|
/// <param name="remoteFileName">The remote file name.</param>
|
|
/// <param name="createOptions">The create options.</param>
|
|
public MongoGridFSFileInfo(
|
|
MongoGridFS gridFS,
|
|
string remoteFileName,
|
|
MongoGridFSCreateOptions createOptions
|
|
) {
|
|
this.gridFS = gridFS;
|
|
this.aliases = createOptions.Aliases;
|
|
this.chunkSize = createOptions.ChunkSize == 0 ? gridFS.Settings.ChunkSize : createOptions.ChunkSize;
|
|
this.contentType = createOptions.ContentType;
|
|
this.id = createOptions.Id;
|
|
this.metadata = createOptions.Metadata;
|
|
this.name = remoteFileName;
|
|
this.uploadDate = createOptions.UploadDate;
|
|
this.cached = true; // prevent values from being overwritten by automatic Refresh
|
|
}
|
|
#endregion
|
|
|
|
#region public properties
|
|
/// <summary>
|
|
/// Gets the aliases.
|
|
/// </summary>
|
|
public string[] Aliases {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return aliases;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the chunk size.
|
|
/// </summary>
|
|
public int ChunkSize {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return chunkSize;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the content type.
|
|
/// </summary>
|
|
public string ContentType {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return contentType;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether the GridFS file exists.
|
|
/// </summary>
|
|
public bool Exists {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return exists;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the GridFS file system that contains this GridFS file.
|
|
/// </summary>
|
|
public MongoGridFS GridFS {
|
|
get { return gridFS; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the GridFS file Id.
|
|
/// </summary>
|
|
public BsonValue Id {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return id;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the file lenth.
|
|
/// </summary>
|
|
public int Length {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return length;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the MD5 hash of the file contents.
|
|
/// </summary>
|
|
public string MD5 {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return md5;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the metadata.
|
|
/// </summary>
|
|
public BsonDocument Metadata {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return metadata;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the remote file name.
|
|
/// </summary>
|
|
public string Name {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return name;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the upload date.
|
|
/// </summary>
|
|
public DateTime UploadDate {
|
|
get {
|
|
if (!cached) { Refresh(); }
|
|
return uploadDate;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region public operators
|
|
/// <summary>
|
|
/// Compares two MongoGridFSFileInfos.
|
|
/// </summary>
|
|
/// <param name="lhs">The first MongoGridFSFileInfo.</param>
|
|
/// <param name="rhs">The other MongoGridFSFileInfo.</param>
|
|
/// <returns>True if the two MongoGridFSFileInfos are not equal (or one is null and the other is not).</returns>
|
|
public static bool operator !=(
|
|
MongoGridFSFileInfo lhs,
|
|
MongoGridFSFileInfo rhs
|
|
) {
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two MongoGridFSFileInfos.
|
|
/// </summary>
|
|
/// <param name="lhs">The first MongoGridFSFileInfo.</param>
|
|
/// <param name="rhs">The other MongoGridFSFileInfo.</param>
|
|
/// <returns>True if the two MongoGridFSFileInfos are equal (or both null).</returns>
|
|
public static bool operator ==(
|
|
MongoGridFSFileInfo lhs,
|
|
MongoGridFSFileInfo rhs
|
|
) {
|
|
return object.Equals(lhs, rhs);
|
|
}
|
|
#endregion
|
|
|
|
#region public methods
|
|
/// <summary>
|
|
/// Appends UTF-8 encoded text to an existing GridFS file.
|
|
/// </summary>
|
|
/// <returns>A StreamWriter.</returns>
|
|
public StreamWriter AppendText() {
|
|
Stream stream = Open(FileMode.Append, FileAccess.Write);
|
|
return new StreamWriter(stream, Encoding.UTF8);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies a GridFS file.
|
|
/// </summary>
|
|
/// <param name="destFileName">The destination file name.</param>
|
|
/// <returns>The file info of the new GridFS file.</returns>
|
|
public MongoGridFSFileInfo CopyTo(
|
|
string destFileName
|
|
) {
|
|
// copy all createOptions except Aliases (which are considered alternate filenames)
|
|
var createOptions = new MongoGridFSCreateOptions {
|
|
ChunkSize = chunkSize,
|
|
ContentType = contentType,
|
|
Metadata = metadata,
|
|
UploadDate = uploadDate
|
|
};
|
|
return CopyTo(destFileName, createOptions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies a GridFS file.
|
|
/// </summary>
|
|
/// <param name="destFileName">The destination file name.</param>
|
|
/// <param name="createOptions">The create options.</param>
|
|
/// <returns>The file info of the new GridFS file.</returns>
|
|
public MongoGridFSFileInfo CopyTo(
|
|
string destFileName,
|
|
MongoGridFSCreateOptions createOptions
|
|
) {
|
|
// note: we are aware that the data is making a round trip from and back to the server
|
|
// but we choose not to use a script to copy the data locally on the server
|
|
// because that would lock the database for too long
|
|
var stream = OpenRead();
|
|
return gridFS.Upload(stream, destFileName, createOptions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates or overwrites a GridFS file.
|
|
/// </summary>
|
|
/// <returns>A stream.</returns>
|
|
public MongoGridFSStream Create() {
|
|
return Open(FileMode.Create, FileAccess.ReadWrite);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates or opens a GridFS file for writing UTF-8 encoded text.
|
|
/// </summary>
|
|
/// <returns>A stream.</returns>
|
|
public StreamWriter CreateText() {
|
|
var stream = Create();
|
|
return new StreamWriter(stream, Encoding.UTF8);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a GridFS file.
|
|
/// </summary>
|
|
public void Delete() {
|
|
if (Exists) {
|
|
using (gridFS.Database.RequestStart(false)) { // not slaveOk
|
|
gridFS.EnsureIndexes();
|
|
gridFS.Files.Remove(Query.EQ("_id", id), gridFS.Settings.SafeMode);
|
|
gridFS.Chunks.Remove(Query.EQ("files_id", id), gridFS.Settings.SafeMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares this MongoGridFSFileInfo to another MongoGridFSFileInfo.
|
|
/// </summary>
|
|
/// <param name="rhs">The other MongoGridFSFileInfo.</param>
|
|
/// <returns>True if the two MongoGridFSFileInfos are equal.</returns>
|
|
public bool Equals(
|
|
MongoGridFSFileInfo rhs
|
|
) {
|
|
if (rhs == null) { return false; }
|
|
return
|
|
(this.aliases == null && rhs.aliases == null || this.aliases != null && rhs.aliases != null && this.aliases.SequenceEqual(rhs.aliases)) &&
|
|
this.chunkSize == rhs.chunkSize &&
|
|
this.contentType == rhs.contentType &&
|
|
this.id == rhs.id &&
|
|
this.length == rhs.length &&
|
|
this.md5 == rhs.md5 &&
|
|
this.metadata == rhs.metadata &&
|
|
this.name == rhs.name &&
|
|
this.uploadDate == rhs.uploadDate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares this MongoGridFSFileInfo to another object.
|
|
/// </summary>
|
|
/// <param name="obj">The other object.</param>
|
|
/// <returns>True if the other object is a MongoGridFSFileInfo and equal to this one.</returns>
|
|
public override bool Equals(object obj) {
|
|
return Equals(obj as MongoGridFSFileInfo); // works even if obj is null
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the hash code.
|
|
/// </summary>
|
|
/// <returns>The hash code.</returns>
|
|
public override int GetHashCode() {
|
|
// see Effective Java by Joshua Bloch
|
|
int hash = 17;
|
|
hash = 37 * hash + ((aliases == null) ? 0 : aliases.GetHashCode());
|
|
hash = 37 * hash + chunkSize.GetHashCode();
|
|
hash = 37 * hash + ((contentType == null) ? 0 : contentType.GetHashCode());
|
|
hash = 37 * hash + ((id == null) ? 0 : id.GetHashCode());
|
|
hash = 37 * hash + length.GetHashCode();
|
|
hash = 37 * hash + ((md5 == null) ? 0 : md5.GetHashCode());
|
|
hash = 37 * hash + ((metadata == null) ? 0 : metadata.GetHashCode());
|
|
hash = 37 * hash + name.GetHashCode();
|
|
hash = 37 * hash + uploadDate.GetHashCode();
|
|
return hash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the most recent version of a GridFS file.
|
|
/// </summary>
|
|
/// <param name="destFileName">The destination file name.</param>
|
|
public void MoveTo(
|
|
string destFileName
|
|
) {
|
|
var query = Query.EQ("_id", id);
|
|
var update = Update.Set("filename", destFileName);
|
|
gridFS.Files.Update(query, update, gridFS.Settings.SafeMode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a GridFS file with the specified mode.
|
|
/// </summary>
|
|
/// <param name="mode">The mode.</param>
|
|
/// <returns>A stream.</returns>
|
|
public MongoGridFSStream Open(
|
|
FileMode mode
|
|
) {
|
|
return Open(mode, FileAccess.ReadWrite);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a GridFS file with the specified mode and access.
|
|
/// </summary>
|
|
/// <param name="mode">The mode.</param>
|
|
/// <param name="access">The access.</param>
|
|
/// <returns>A stream.</returns>
|
|
public MongoGridFSStream Open(
|
|
FileMode mode,
|
|
FileAccess access
|
|
) {
|
|
return new MongoGridFSStream(this, mode, access);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens an existing GridFS file for reading.
|
|
/// </summary>
|
|
/// <returns>A stream.</returns>
|
|
public MongoGridFSStream OpenRead() {
|
|
return Open(FileMode.Open, FileAccess.Read);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens an existing UTF-8 encoded text GridFS file for reading.
|
|
/// </summary>
|
|
/// <returns>A stream reader.</returns>
|
|
public StreamReader OpenText() {
|
|
Stream stream = Open(FileMode.Open, FileAccess.Read);
|
|
return new StreamReader(stream, Encoding.UTF8);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens an existing GridFS file for writing.
|
|
/// </summary>
|
|
/// <returns>A stream.</returns>
|
|
public MongoGridFSStream OpenWrite() {
|
|
return Open(FileMode.OpenOrCreate, FileAccess.Write);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refreshes the GridFS file info from the server.
|
|
/// </summary>
|
|
public void Refresh() {
|
|
MongoCursor<BsonDocument> cursor;
|
|
if (id != null) {
|
|
cursor = gridFS.Files.Find(Query.EQ("_id", id));
|
|
} else {
|
|
gridFS.EnsureIndexes();
|
|
cursor = gridFS.Files.Find(Query.EQ("filename", name)).SetSortOrder(SortBy.Descending("uploadDate"));
|
|
}
|
|
var fileInfo = cursor.SetLimit(1).FirstOrDefault();
|
|
CacheFileInfo(fileInfo); // fileInfo will be null if file does not exist
|
|
}
|
|
#endregion
|
|
|
|
#region internal methods
|
|
internal void SetId(
|
|
BsonValue id
|
|
) {
|
|
if (this.id == null) {
|
|
this.id = id;
|
|
} else {
|
|
throw new InvalidOperationException("FileInfo already has an Id.");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region private methods
|
|
private void CacheFileInfo(
|
|
BsonDocument fileInfo
|
|
) {
|
|
if (fileInfo == null) {
|
|
// leave aliases, chunkSize, contentType, id, metadata and name alone (they might be needed to create a new file)
|
|
exists = false;
|
|
length = 0;
|
|
md5 = null;
|
|
uploadDate = default(DateTime);
|
|
} else {
|
|
var aliasesValue = fileInfo["aliases", null];
|
|
if (aliasesValue != null && !aliasesValue.IsBsonNull) {
|
|
var list = new List<string>();
|
|
foreach (var alias in aliasesValue.AsBsonArray) {
|
|
list.Add(alias.AsString);
|
|
}
|
|
aliases = list.ToArray();
|
|
} else {
|
|
aliases = null;
|
|
}
|
|
chunkSize = fileInfo["chunkSize"].ToInt32();
|
|
var contentTypeValue = fileInfo["contentType", null];
|
|
if (contentTypeValue != null && !contentTypeValue.IsBsonNull) {
|
|
contentType = contentTypeValue.AsString;
|
|
} else {
|
|
contentType = null;
|
|
}
|
|
exists = true;
|
|
id = fileInfo["_id"];
|
|
length = fileInfo["length"].ToInt32();
|
|
var md5Value = fileInfo["md5", null];
|
|
if (md5Value != null && !md5Value.IsBsonNull) {
|
|
md5 = md5Value.AsString;
|
|
} else {
|
|
md5 = null;
|
|
}
|
|
var metadataValue = fileInfo["metadata", null];
|
|
if (metadataValue != null && !metadataValue.IsBsonNull) {
|
|
metadata = metadataValue.AsBsonDocument;
|
|
} else {
|
|
metadata = null;
|
|
}
|
|
var filenameValue = fileInfo["filename", null];
|
|
if (filenameValue != null && !filenameValue.IsBsonNull) {
|
|
name = filenameValue.AsString;
|
|
} else {
|
|
name = null;
|
|
}
|
|
uploadDate = fileInfo["uploadDate"].AsDateTime;
|
|
}
|
|
cached = true;
|
|
}
|
|
#endregion
|
|
|
|
#region explicit interface implementations
|
|
object IBsonSerializable.Deserialize(
|
|
BsonReader bsonReader,
|
|
Type nominalType,
|
|
IBsonSerializationOptions options
|
|
) {
|
|
MongoGridFS gridFS = ((SerializationOptions) options).GridFS;
|
|
var fileInfo = BsonDocument.ReadFrom(bsonReader);
|
|
return new MongoGridFSFileInfo(gridFS, fileInfo);
|
|
}
|
|
|
|
bool IBsonSerializable.GetDocumentId(
|
|
out object id,
|
|
out Type idNominalType,
|
|
out IIdGenerator idGenerator
|
|
) {
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
void IBsonSerializable.Serialize(
|
|
BsonWriter bsonWriter,
|
|
Type nominalType,
|
|
IBsonSerializationOptions options
|
|
) {
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
void IBsonSerializable.SetDocumentId(
|
|
object id
|
|
) {
|
|
throw new NotSupportedException();
|
|
}
|
|
#endregion
|
|
|
|
#region nested classes
|
|
internal class SerializationOptions : IBsonSerializationOptions {
|
|
internal MongoGridFS GridFS;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|