У меня есть веб-приложение Java Google App Engine, которое позволяет пользователям загружать изображения. Локально работает отлично. Однако, как только я развертываю его в «облаке» и загружаю изображение, я получаю следующую ошибку:
java.lang.IllegalArgumentException: нельзя работать с несколькими группами сущностей в одной транзакции.
Я использую хранилище BLOB-объектов для хранения изображений (справочник Blobstore ). Мой метод ниже:
@RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST)
@ResponseBody
public String uploadWelcomeImage(@RequestParam("id") long id,
HttpServletRequest request) throws IOException, ServletException {
byte[] bytes = IOUtils.toByteArray(request.getInputStream());
Key parentKey = KeyFactory.createKey(ParentClass.class.getSimpleName(),
id);
String blobKey = imageService.saveImageToBlobStore(bytes);
imageService.save(blobKey, parentKey);
return "{success:true, id:\"" + blobKey + "\"}";
}
Вы заметите, что этот метод сначала вызывает «imageService.saveImageToBlobStore». Это то, что на самом деле сохраняет байты изображения. Метод "imageService.save" берет сгенерированный blobKey и заключает его в объект ImageFile, который является объектом, содержащим String blobKey. Мой веб-сайт ссылается на imageFile.blobKey, чтобы получить правильное изображение для отображения. "saveImageToBlobStore" выглядит так:
@Transactional
public String saveImageToBlobStore(byte[] bytes) {
// Get a file service
FileService fileService = FileServiceFactory.getFileService();
// Create a new Blob file with mime-type "text/plain"
AppEngineFile file = null;
try {
file = fileService.createNewBlobFile("image/jpeg");
} catch (IOException e) {
e.printStackTrace();
}
// Open a channel to write to it
boolean lock = true;
FileWriteChannel writeChannel = null;
try {
writeChannel = fileService.openWriteChannel(file, lock);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (FinalizationException e) {
e.printStackTrace();
} catch (LockException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// This time we write to the channel using standard Java
try {
writeChannel.write(ByteBuffer.wrap(bytes));
} catch (IOException e) {
e.printStackTrace();
}
// Now finalize
try {
writeChannel.closeFinally();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// Now read from the file using the Blobstore API
BlobKey blobKey = fileService.getBlobKey(file);
while (blobKey == null) { //this is hacky, but necessary as sometimes the blobkey isn't available right away
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
blobKey = fileService.getBlobKey(file);
}
// return
return blobKey.getKeyString();
}
Мой другой метод сохранения выглядит так:
public void save(String imageFileBlobKey, Key parentKey) {
DatastoreService datastore = DatastoreServiceFactory
.getDatastoreService();
Entity imageFileEntity = new Entity("ImageFile", parentKey);
imageFileEntity.setProperty("blobKey", imageFileBlobKey);
datastore.put(imageFileEntity);
}
Как я уже говорил, он работает локально, но не развернут. Ошибка возникает при вызове saveImageToBlobstore, в частности, в «fileservice.getBlobKey (файл)». Комментирование этой строки устраняет ошибку, но мне нужна эта строка, чтобы сохранить байты изображения в хранилище больших двоичных объектов.
Я также пытался комментировать другие строки (см. ниже), но безуспешно. Та же ошибка для этого:
@RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST)
@ResponseBody
public String uploadWelcomeImage(@RequestParam("id") long id,
HttpServletRequest request) throws IOException, ServletException {
//byte[] bytes = IOUtils.toByteArray(request.getInputStream());
//Key parentKey= KeyFactory.createKey(ParentClass.class.getSimpleName(),
//id);
byte[] bytes = {0,1,0};
String blobKey = imageService.saveImageToBlobStore(bytes);
//imageService.save(blobKey, parentKey);
return "{success:true, id:\"" + blobKey + "\"}";
}
Любые идеи? Я использую GAE 1.5.2. Спасибо!
ОБНОВЛЕНИЕ, НАЙДЕНО РЕШЕНИЕ: я взял некоторый код из транзакционного "saveImageToBlobStore" и переместил его на уровень выше. Увидеть ниже:
@RequestMapping(value = "/ajax/uploadWelcomeImage", method = RequestMethod.POST)
@ResponseBody
public String uploadWelcomeImage(@RequestParam("id") long id,
HttpServletRequest request) throws IOException, ServletException {
byte[] bytes = IOUtils.toByteArray(request.getInputStream());
Key parentKey = KeyFactory.createKey(ParentClass.class.getSimpleName(),
id);
//pulled the following out of transactional method:
AppEngineFile file = imageService.saveImageToBlobStore(bytes);
FileService fileService = FileServiceFactory.getFileService();
//code below is similar to before//////////////
BlobKey key = fileService.getBlobKey(file);
String keyString = key.getKeyString();
imageService.save(keyString, parentKey);
return "{success:true, id:\"" + keyString + "\"}";