Changed post drafts to maintain a store a unique id for each file upload to fix issues with duplicate file names
Этот коммит содержится в:
@@ -71,7 +71,9 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
files := m.File["files"]
|
files := m.File["files"]
|
||||||
|
|
||||||
resStruct := &model.FileUploadResponse{
|
resStruct := &model.FileUploadResponse{
|
||||||
Filenames: []string{}}
|
Filenames: []string{},
|
||||||
|
ClientIds: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
imageNameList := []string{}
|
imageNameList := []string{}
|
||||||
imageDataList := [][]byte{}
|
imageDataList := [][]byte{}
|
||||||
@@ -113,6 +115,10 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
|
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, clientId := range props["client_ids"] {
|
||||||
|
resStruct.ClientIds = append(resStruct.ClientIds, clientId)
|
||||||
|
}
|
||||||
|
|
||||||
fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)
|
fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)
|
||||||
|
|
||||||
w.Write([]byte(resStruct.ToJson()))
|
w.Write([]byte(resStruct.ToJson()))
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ var (
|
|||||||
|
|
||||||
type FileUploadResponse struct {
|
type FileUploadResponse struct {
|
||||||
Filenames []string `json:"filenames"`
|
Filenames []string `json:"filenames"`
|
||||||
|
ClientIds []string `json:"client_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {
|
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ module.exports = React.createClass({
|
|||||||
$(".post-right__scroll").perfectScrollbar('update');
|
$(".post-right__scroll").perfectScrollbar('update');
|
||||||
this.setState({messageText: messageText});
|
this.setState({messageText: messageText});
|
||||||
},
|
},
|
||||||
handleUploadStart: function(filenames, channel_id) {
|
handleUploadStart: function(clientIds, channel_id) {
|
||||||
var draft = PostStore.getCommentDraft(this.props.rootId);
|
var draft = PostStore.getCommentDraft(this.props.rootId);
|
||||||
if (!draft) {
|
if (!draft) {
|
||||||
draft = {};
|
draft = {};
|
||||||
@@ -104,12 +104,12 @@ module.exports = React.createClass({
|
|||||||
draft['previews'] = [];
|
draft['previews'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(filenames);
|
draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
|
||||||
PostStore.storeCommentDraft(this.props.rootId, draft);
|
PostStore.storeCommentDraft(this.props.rootId, draft);
|
||||||
|
|
||||||
this.setState({uploadsInProgress: draft['uploadsInProgress']});
|
this.setState({uploadsInProgress: draft['uploadsInProgress']});
|
||||||
},
|
},
|
||||||
handleFileUploadComplete: function(filenames, channel_id) {
|
handleFileUploadComplete: function(filenames, clientIds, channel_id) {
|
||||||
var draft = PostStore.getCommentDraft(this.props.rootId);
|
var draft = PostStore.getCommentDraft(this.props.rootId);
|
||||||
if (!draft) {
|
if (!draft) {
|
||||||
draft = {};
|
draft = {};
|
||||||
@@ -119,18 +119,8 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove each finished file from uploads
|
// remove each finished file from uploads
|
||||||
for (var i = 0; i < filenames.length; i++) {
|
for (var i = 0; i < clientIds.length; i++) {
|
||||||
var filename = filenames[i];
|
var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
|
||||||
|
|
||||||
// filenames returned by the server include a path while stored uploads only have the actual file name
|
|
||||||
var index = -1;
|
|
||||||
for (var j = 0; j < draft['uploadsInProgress'].length; j++) {
|
|
||||||
var upload = draft['uploadsInProgress'][j];
|
|
||||||
if (upload.indexOf(filename, upload.length - filename.length)) {
|
|
||||||
index = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
draft['uploadsInProgress'].splice(index, 1);
|
draft['uploadsInProgress'].splice(index, 1);
|
||||||
@@ -148,20 +138,20 @@ module.exports = React.createClass({
|
|||||||
clearPreviews: function() {
|
clearPreviews: function() {
|
||||||
this.setState({previews: []});
|
this.setState({previews: []});
|
||||||
},
|
},
|
||||||
removePreview: function(filename) {
|
removePreview: function(id) {
|
||||||
var previews = this.state.previews;
|
var previews = this.state.previews;
|
||||||
var uploadsInProgress = this.state.uploadsInProgress;
|
var uploadsInProgress = this.state.uploadsInProgress;
|
||||||
|
|
||||||
// this can be either an uploaded file or an in progress upload that we need to remove
|
// id can either be the path of an uploaded file or the client id of an in progress upload
|
||||||
var index = previews.indexOf(filename);
|
var index = previews.indexOf(id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
previews.splice(index, 1);
|
previews.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
index = uploadsInProgress.indexOf(filename);
|
index = uploadsInProgress.indexOf(id);
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
uploadsInProgress.splice(index, 1);
|
uploadsInProgress.splice(index, 1);
|
||||||
this.refs.fileUpload.cancelUpload(filename);
|
this.refs.fileUpload.cancelUpload(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ module.exports = React.createClass({
|
|||||||
$(".post-list-holder-by-time").css("height", height + "px");
|
$(".post-list-holder-by-time").css("height", height + "px");
|
||||||
$(window).trigger('resize');
|
$(window).trigger('resize');
|
||||||
},
|
},
|
||||||
handleUploadStart: function(filenames, channel_id) {
|
handleUploadStart: function(clientIds, channel_id) {
|
||||||
var draft = PostStore.getDraft(channel_id);
|
var draft = PostStore.getDraft(channel_id);
|
||||||
if (!draft) {
|
if (!draft) {
|
||||||
draft = {};
|
draft = {};
|
||||||
@@ -137,12 +137,12 @@ module.exports = React.createClass({
|
|||||||
draft['previews'] = [];
|
draft['previews'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(filenames);
|
draft['uploadsInProgress'] = draft['uploadsInProgress'].concat(clientIds);
|
||||||
PostStore.storeDraft(channel_id, draft);
|
PostStore.storeDraft(channel_id, draft);
|
||||||
|
|
||||||
this.setState({uploadsInProgress: draft['uploadsInProgress']});
|
this.setState({uploadsInProgress: draft['uploadsInProgress']});
|
||||||
},
|
},
|
||||||
handleFileUploadComplete: function(filenames, channel_id) {
|
handleFileUploadComplete: function(filenames, clientIds, channel_id) {
|
||||||
var draft = PostStore.getDraft(channel_id);
|
var draft = PostStore.getDraft(channel_id);
|
||||||
if (!draft) {
|
if (!draft) {
|
||||||
draft = {};
|
draft = {};
|
||||||
@@ -152,18 +152,8 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove each finished file from uploads
|
// remove each finished file from uploads
|
||||||
for (var i = 0; i < filenames.length; i++) {
|
for (var i = 0; i < clientIds.length; i++) {
|
||||||
var filename = filenames[i];
|
var index = draft['uploadsInProgress'].indexOf(clientIds[i]);
|
||||||
|
|
||||||
// filenames returned by the server include a path while stored uploads only have the actual file name
|
|
||||||
var index = -1;
|
|
||||||
for (var j = 0; j < draft['uploadsInProgress'].length; j++) {
|
|
||||||
var upload = draft['uploadsInProgress'][j];
|
|
||||||
if (upload.indexOf(filename, upload.length - filename.length)) {
|
|
||||||
index = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
draft['uploadsInProgress'].splice(index, 1);
|
draft['uploadsInProgress'].splice(index, 1);
|
||||||
@@ -178,20 +168,20 @@ module.exports = React.createClass({
|
|||||||
handleUploadError: function(err) {
|
handleUploadError: function(err) {
|
||||||
this.setState({ server_error: err });
|
this.setState({ server_error: err });
|
||||||
},
|
},
|
||||||
removePreview: function(filename) {
|
removePreview: function(id) {
|
||||||
var previews = this.state.previews;
|
var previews = this.state.previews;
|
||||||
var uploadsInProgress = this.state.uploadsInProgress;
|
var uploadsInProgress = this.state.uploadsInProgress;
|
||||||
|
|
||||||
// this can be either an uploaded file or an in progress upload that we need to remove
|
// id can either be the path of an uploaded file or the client id of an in progress upload
|
||||||
var index = previews.indexOf(filename);
|
var index = previews.indexOf(id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
previews.splice(index, 1);
|
previews.splice(index, 1);
|
||||||
} else {
|
} else {
|
||||||
index = uploadsInProgress.indexOf(filename);
|
index = uploadsInProgress.indexOf(id);
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
uploadsInProgress.splice(index, 1);
|
uploadsInProgress.splice(index, 1);
|
||||||
this.refs.fileUpload.cancelUpload(filename);
|
this.refs.fileUpload.cancelUpload(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ var Constants = require('../utils/constants.jsx');
|
|||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
handleRemove: function(e) {
|
handleRemove: function(e) {
|
||||||
var previewDiv = e.target.parentNode.parentNode;
|
var previewDiv = e.target.parentNode.parentNode;
|
||||||
this.props.onRemove(previewDiv.getAttribute('data-filename'));
|
|
||||||
|
if (previewDiv.hasAttribute('data-filename')) {
|
||||||
|
this.props.onRemove(previewDiv.getAttribute('data-filename'));
|
||||||
|
} else if (previewDiv.hasAttribute('data-client-id')) {
|
||||||
|
this.props.onRemove(previewDiv.getAttribute('data-client-id'));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var previews = [];
|
var previews = [];
|
||||||
@@ -43,9 +48,9 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.props.uploadsInProgress.forEach(function(filename) {
|
this.props.uploadsInProgress.forEach(function(clientId) {
|
||||||
previews.push(
|
previews.push(
|
||||||
<div className="preview-div" data-filename={filename}>
|
<div className="preview-div" data-client-id={clientId}>
|
||||||
<img className="spinner" src="/static/images/load.gif"/>
|
<img className="spinner" src="/static/images/load.gif"/>
|
||||||
<a className="remove-preview" onClick={this.handleRemove}><i className="glyphicon glyphicon-remove"/></a>
|
<a className="remove-preview" onClick={this.handleRemove}><i className="glyphicon glyphicon-remove"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,19 +39,23 @@ module.exports = React.createClass({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate a unique id that can be used by other components to refer back to this file upload
|
||||||
|
var clientId = utils.generateId();
|
||||||
|
|
||||||
// Prepare data to be uploaded.
|
// Prepare data to be uploaded.
|
||||||
formData = new FormData();
|
formData = new FormData();
|
||||||
formData.append('channel_id', channel_id);
|
formData.append('channel_id', channel_id);
|
||||||
formData.append('files', files[i], files[i].name);
|
formData.append('files', files[i], files[i].name);
|
||||||
|
formData.append('client_ids', clientId);
|
||||||
|
|
||||||
var request = client.uploadFile(formData,
|
var request = client.uploadFile(formData,
|
||||||
function(data) {
|
function(data) {
|
||||||
parsedData = $.parseJSON(data);
|
parsedData = $.parseJSON(data);
|
||||||
this.props.onFileUpload(parsedData['filenames'], channel_id);
|
this.props.onFileUpload(parsedData['filenames'], parsedData['client_ids'], channel_id);
|
||||||
|
|
||||||
var requests = this.state.requests;
|
var requests = this.state.requests;
|
||||||
for (var i = 0; i < parsedData['filenames'].length; i++) {
|
for (var i = 0; i < parsedData['client_ids'].length; i++) {
|
||||||
delete requests[utils.getFileName(parsedData['filenames'][i])];
|
delete requests[parsedData['client_ids'][i]];
|
||||||
}
|
}
|
||||||
this.setState({requests: requests});
|
this.setState({requests: requests});
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
@@ -61,10 +65,10 @@ module.exports = React.createClass({
|
|||||||
);
|
);
|
||||||
|
|
||||||
var requests = this.state.requests;
|
var requests = this.state.requests;
|
||||||
requests[files[i].name] = request;
|
requests[clientId] = request;
|
||||||
this.setState({requests: requests});
|
this.setState({requests: requests});
|
||||||
|
|
||||||
this.props.onUploadStart([files[i].name], channel_id);
|
this.props.onUploadStart([clientId], channel_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear file input for all modern browsers
|
// clear file input for all modern browsers
|
||||||
@@ -123,6 +127,9 @@ module.exports = React.createClass({
|
|||||||
|
|
||||||
var channel_id = ChannelStore.getCurrentId();
|
var channel_id = ChannelStore.getCurrentId();
|
||||||
|
|
||||||
|
// generate a unique id that can be used by other components to refer back to this file upload
|
||||||
|
var clientId = utils.generateId();
|
||||||
|
|
||||||
formData = new FormData();
|
formData = new FormData();
|
||||||
formData.append('channel_id', channel_id);
|
formData.append('channel_id', channel_id);
|
||||||
var d = new Date();
|
var d = new Date();
|
||||||
@@ -130,15 +137,16 @@ module.exports = React.createClass({
|
|||||||
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
|
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes());
|
||||||
var name = "Image Pasted at "+d.getFullYear()+"-"+d.getMonth()+"-"+d.getDate()+" "+hour+"-"+min+"." + ext;
|
var name = "Image Pasted at "+d.getFullYear()+"-"+d.getMonth()+"-"+d.getDate()+" "+hour+"-"+min+"." + ext;
|
||||||
formData.append('files', file, name);
|
formData.append('files', file, name);
|
||||||
|
formData.append('client_ids', clientId);
|
||||||
|
|
||||||
client.uploadFile(formData,
|
var request = client.uploadFile(formData,
|
||||||
function(data) {
|
function(data) {
|
||||||
parsedData = $.parseJSON(data);
|
parsedData = $.parseJSON(data);
|
||||||
self.props.onFileUpload(parsedData['filenames'], channel_id);
|
self.props.onFileUpload(parsedData['filenames'], parsedData['client_ids'], channel_id);
|
||||||
|
|
||||||
var requests = self.state.requests;
|
var requests = self.state.requests;
|
||||||
for (var i = 0; i < parsedData['filenames'].length; i++) {
|
for (var i = 0; i < parsedData['client_ids'].length; i++) {
|
||||||
delete requests[utils.getFileName(parsedData['filenames'][i])];
|
delete requests[parsedData['client_ids'][i]];
|
||||||
}
|
}
|
||||||
self.setState({requests: requests});
|
self.setState({requests: requests});
|
||||||
},
|
},
|
||||||
@@ -148,23 +156,23 @@ module.exports = React.createClass({
|
|||||||
);
|
);
|
||||||
|
|
||||||
var requests = self.state.requests;
|
var requests = self.state.requests;
|
||||||
requests[files[i].name] = request;
|
requests[clientId] = request;
|
||||||
self.setState({requests: requests});
|
self.setState({requests: requests});
|
||||||
|
|
||||||
self.props.onUploadStart([name], channel_id);
|
self.props.onUploadStart([clientId], channel_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
cancelUpload: function(filename) {
|
cancelUpload: function(clientId) {
|
||||||
var requests = this.state.requests;
|
var requests = this.state.requests;
|
||||||
var request = requests[filename];
|
var request = requests[clientId];
|
||||||
|
|
||||||
if (request) {
|
if (request) {
|
||||||
request.abort();
|
request.abort();
|
||||||
|
|
||||||
delete requests[filename];
|
delete requests[clientId];
|
||||||
this.setState({requests: requests});
|
this.setState({requests: requests});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -913,3 +913,24 @@ module.exports.getFileName = function(path) {
|
|||||||
var split = path.split('/');
|
var split = path.split('/');
|
||||||
return split[split.length - 1];
|
return split[split.length - 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generates a RFC-4122 version 4 compliant globally unique identifier.
|
||||||
|
module.exports.generateId = function() {
|
||||||
|
// implementation taken from http://stackoverflow.com/a/2117523
|
||||||
|
var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
||||||
|
|
||||||
|
id = id.replace(/[xy]/g, function(c) {
|
||||||
|
var r = Math.floor(Math.random() * 16);
|
||||||
|
|
||||||
|
var v;
|
||||||
|
if (c === 'x') {
|
||||||
|
v = r;
|
||||||
|
} else {
|
||||||
|
v = r & 0x3 | 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user