6 changed files with 204 additions and 3 deletions
@ -0,0 +1,135 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/rand" |
||||||
|
"encoding/base64" |
||||||
|
"encoding/binary" |
||||||
|
"log" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
var imageFilenames map[string]string |
||||||
|
|
||||||
|
type Image struct { |
||||||
|
updateIdx int |
||||||
|
lastChange time.Time |
||||||
|
lastChangeId string |
||||||
|
userid string |
||||||
|
mimetype string |
||||||
|
data []byte |
||||||
|
} |
||||||
|
|
||||||
|
type ImageCache interface { |
||||||
|
Update(userId string, image string) string |
||||||
|
|
||||||
|
Get(imageId string) *Image |
||||||
|
|
||||||
|
DeleteUserImage(userId string) |
||||||
|
} |
||||||
|
|
||||||
|
type imageCache struct { |
||||||
|
images map[string]*Image |
||||||
|
userImages map[string]string |
||||||
|
mutex sync.RWMutex |
||||||
|
} |
||||||
|
|
||||||
|
func NewImageCache() ImageCache { |
||||||
|
result := &imageCache{} |
||||||
|
result.images = make(map[string]*Image) |
||||||
|
result.userImages = make(map[string]string) |
||||||
|
if imageFilenames == nil { |
||||||
|
imageFilenames = map[string]string{ |
||||||
|
"image/png": "picture.png", |
||||||
|
"image/jpeg": "picture.jpg", |
||||||
|
"image/gif": "picture.gif", |
||||||
|
} |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
func (self *imageCache) Update(userId string, image string) string { |
||||||
|
var mimetype string = "image/x-unknown" |
||||||
|
pos := strings.Index(image, ";") |
||||||
|
if pos != -1 { |
||||||
|
mimetype = image[:pos] |
||||||
|
image = image[pos+1:] |
||||||
|
} |
||||||
|
pos = strings.Index(image, ",") |
||||||
|
var decoded []byte |
||||||
|
var err error |
||||||
|
if pos != -1 { |
||||||
|
encoding := image[:pos] |
||||||
|
switch encoding { |
||||||
|
case "base64": |
||||||
|
decoded, err = base64.StdEncoding.DecodeString(image[pos+1:]) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
default: |
||||||
|
log.Println("Unknown encoding", encoding) |
||||||
|
return "" |
||||||
|
} |
||||||
|
} else { |
||||||
|
decoded = []byte(image[pos+1:]) |
||||||
|
} |
||||||
|
var img *Image |
||||||
|
self.mutex.RLock() |
||||||
|
result, ok := self.userImages[userId] |
||||||
|
if !ok { |
||||||
|
self.mutex.RUnlock() |
||||||
|
imageId := make([]byte, 15, 15) |
||||||
|
if _, err = rand.Read(imageId); err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
result = base64.URLEncoding.EncodeToString(imageId) |
||||||
|
img = &Image{userid: userId} |
||||||
|
self.mutex.Lock() |
||||||
|
resultTmp, ok := self.userImages[userId] |
||||||
|
if !ok { |
||||||
|
self.userImages[userId] = result |
||||||
|
self.images[result] = img |
||||||
|
} else { |
||||||
|
result = resultTmp |
||||||
|
img = self.images[result] |
||||||
|
} |
||||||
|
self.mutex.Unlock() |
||||||
|
} else { |
||||||
|
img = self.images[result] |
||||||
|
self.mutex.RUnlock() |
||||||
|
} |
||||||
|
if mimetype != img.mimetype || !bytes.Equal(img.data, decoded) { |
||||||
|
img.updateIdx++ |
||||||
|
img.lastChange = time.Now() |
||||||
|
tmp := make([]byte, binary.MaxVarintLen64) |
||||||
|
count := binary.PutUvarint(tmp, uint64(img.lastChange.UnixNano())) |
||||||
|
img.lastChangeId = base64.URLEncoding.EncodeToString(tmp[:count]) |
||||||
|
img.mimetype = mimetype |
||||||
|
img.data = decoded |
||||||
|
} |
||||||
|
result += "/" + img.lastChangeId |
||||||
|
filename, ok := imageFilenames[mimetype] |
||||||
|
if ok { |
||||||
|
result += "/" + filename |
||||||
|
} |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
func (self *imageCache) Get(imageId string) *Image { |
||||||
|
self.mutex.RLock() |
||||||
|
image := self.images[imageId] |
||||||
|
self.mutex.RUnlock() |
||||||
|
return image |
||||||
|
} |
||||||
|
|
||||||
|
func (self *imageCache) DeleteUserImage(userId string) { |
||||||
|
self.mutex.Lock() |
||||||
|
imageId, ok := self.userImages[userId] |
||||||
|
if ok { |
||||||
|
delete(self.userImages, userId) |
||||||
|
delete(self.images, imageId) |
||||||
|
} |
||||||
|
self.mutex.Unlock() |
||||||
|
} |
||||||
@ -1,5 +1,5 @@ |
|||||||
<div class="buddy withSubline" ng-click="doDefault(user.Id)"> |
<div class="buddy withSubline" ng-click="doDefault(user.Id)"> |
||||||
<div class="buddyimage"><i class="fa fa-user fa-3x"/><img ng-show="status.buddyPicture" alt="" ng-src="{{status.buddyPicture}}"/></div> |
<div class="buddyimage"><i class="fa fa-user fa-3x"/><img ng-show="status.buddyPicture" alt ng-src="{{status.buddyPicture}}" width="46" height="46"/></div> |
||||||
<div class="buddy1">{{user.Id|displayName}}</div> |
<div class="buddy1">{{user.Id|displayName}}</div> |
||||||
<div class="buddy2">{{user.Ua}}</div> |
<div class="buddy2">{{user.Ua}}</div> |
||||||
</div> |
</div> |
||||||
Loading…
Reference in new issue