ブラウザでの大容量ファイルスライスアップロード(Javaサーバサイド実装)
WeChat検索:"20人の学生"公開番号、成長の異なる道をたどることを歓迎します。
Webサイトの最も基本的な機能はファイルのアップロードですが、大きなファイルになると、従来の方法でWeb上にアップロードすると大変なことになるので、大きなファイルを分割してアップロードすることができます。主なアイデアは、1.アップロード時に大きなファイルをスライスする、2.バラバラにアップロードする、3.バラバラにしたファイルを結合する、です。
考え方は比較的明快でシンプルなのですが、いくつか疑問があります。1. 大きなファイルをどのようにスライスするのか?2. 2. スライスの記録と保存はどのように行うのか?3. 各スライスファイルの一意性と順序をチェックする方法は?4. ファイルをマージする方法は?
大きなファイルをスライスする方法については、主にフロントエンドで解決されます。フロントエンドで必要なことを実現するには、BaiduのWebUploaderを使用することをお勧めします。
シャーディング後のファイルの保存には、バイトビットに対応した各チャンクの状態を保存する一時ファイルストレージを使用しています。
MD5コードは、簡単に言うと各ファイルのIDのようなもので、異なるファイルはそれぞれ固有のMD5コードを持っていると理解すればよいでしょう。
ファイルをマージする場合、フロントエンドでは、ファイルを分割した後、サーバーにマージを要求する際、リクエストに分割番号とサイズを持参し、サーバーはリクエストデータに与えられた分割番号と各分割サイズに応じて開始位置を計算し、読み込んだファイルの断片データでファイルを書き込む必要があります。ここで、マージされたファイルには、現在のネットワークディレクトリの下のパスと、実際のパーマネントパスの2つが格納されます(目的は、2回目の転送の機能を実現するためです)。
フロントエンドのスライスのコードは掲載されていませんが、主にBaiduのWebUploaderを使用しています。
以下は、主なサーバーサイドのコードです。
ファイルのアップロード
/**
* Uploading files
*
* @param file file
* @param wholeMd5 file overall md5 code
* @param name file name
* @param type file type
* @param lastModifiedDate Upload time
* @param size file size
* @param chunks Number of chunks in the file
* @param chunk The chunk being executed
*/
@ApiOperation(value = "FileUpload", hidden = true)
@IgnoreUserToken
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
@PostMapping(value = "upload")
public ResponseEntity<Integer> fileUpload(@ApiParam(name = "file") @RequestPart MultipartFile file,
@ApiParam(name = "md5") @RequestParam String wholeMd5,
@ApiParam(name = "name") @RequestParam String name,
@ApiParam(name = "type") @RequestParam String type,
@ApiParam(name = "date") @RequestParam Date lastModifiedDate,
@ApiParam(name = "size") @RequestParam long size,
@ApiParam(name = "Start position") @RequestParam long start,
@ApiParam(name = "end position") @RequestParam long end,
@ApiParam(name = "Total chunks") @RequestParam(name = "chunks", defaultValue = "1") int chunks,
@ApiParam(name = "chunks, starting from 0") @RequestParam(name = "chunk", defaultValue = "0") int chunk) {
try {
log.info("File upload started");
this.fileServiceImpl.fileUpload(file.getInputStream(), wholeMd5, name, type, lastModifiedDate, size, chunks, chunk, start, end);
return ResponseEntity.ok(1);
} catch (Exception e) {
return new ResponseEntity(RestError.IO_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Override
public boolean fileUpload(InputStream fileIS,
String wholeMd5,
String name, String type,
Date lastModifiedDate, long size,
int chunks,
int chunk,
long start,
long end) throws Exception {
boolean result = false;
try {
File tempDirFile = new File(fileDir, TEMP_DIR);
if (!tempDirFile.exists()) {
tempDirFile.mkdirs();
}
// Block directory folder
File wholeMd5FileDirectory = new File(tempDirFile.getAbsolutePath(), wholeMd5);
if (!wholeMd5FileDirectory.exists()) {
wholeMd5FileDirectory.mkdirs();
}
// chunk file
File chunkFile = new File(wholeMd5FileDirectory.getAbsolutePath(), chunk + FILE_SEPARATOR + chunks + FILE_EXT);
long chunkSize = end - start;
if (!chunkFile.exists() || chunkFile.length() ! = chunkSize) {
// Create a new chunk file
long startTime = System.currentTimeMillis();
log.info("Creating build chunk {} - {} ", start, end);
int length = StreamUtils.copy(fileIS, new FileOutputStream(chunkFile));
long endTime = System.currentTimeMillis();
log.info("Slice upload took {} milliseconds", (endTime - startTime));
if (length == (end - start)) {
result = true;
}
}
} catch (Exception e) {
log.error("Error uploading file {}", e.getCause());
e.printStackTrace();
throw e;
}
return result;
}
/**
* Check the md5 of the file
*
* @param md5 file md5
* @param fileSize file size
* @return
*/
@ApiOperation(value = "checkFileMd5")
@GetMapping(value = "checkFileMd5/{md5}/{fileSize}/{md5CheckLength}")
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
public ResponseEntity<Integer> checkFileMd5(@ApiParam("file md5 code") @PathVariable String md5,
@ApiParam("file size") @PathVariable long fileSize,
@ApiParam("file used to check md5 length") @PathVariable long md5CheckLength) {
try {
log.info("Started checking md5[{}],exists", md5);
return ResponseEntity.ok(this.fileServiceImpl.checkFileMd5(md5, fileSize, md5CheckLength) ? 1 : 0);
} catch (Exception e) {
return new ResponseEntity(RestError.DATABASE_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Override
public boolean checkFileMd5(String md5, long fileSize, long md5CheckLength) {
Optional<UploadFileInfo> uploadFileInfo = this.uploadFileDao.findByMd5AndSize(md5, fileSize);
boolean isExist = false;
if (uploadFileInfo.isPresent()) {
File wholeFile = new File(this.fileDir, uploadFileInfo.get().getDfsPath());
if (wholeFile.exists() && wholeFile.length() == fileSize && md5.equals(FileUtils.md5(wholeFile, 0, md5CheckLength))) {
isExist = true;
}
}
log.info("{} of file {} exists", md5, isExist ? "" : "no");
return isExist;
}
ファイルのMD5を確認する
/**
* Check if the slice exists
*
* @param md5
* @param chunk
* @param chunks
* @param chunkStart
* @param chunkEnd
* @return
*/
@ApiOperation(value = "Check if chunk exists")
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
@GetMapping(value = "checkChunk/{md5}/{blockMd5}/{md5CheckLength}/{chunk}/{chunks}/{chunkStart}/{chunkEnd}")
public ResponseEntity<Integer> checkChunk(@ApiParam("file md5 code") @PathVariable String md5,
@ApiParam("chunked file md5 code") @PathVariable String blockMd5,
@ApiParam("used to check the length of the block file md5 code") @PathVariable long md5CheckLength,
@ApiParam("how many chunks, starting from 0") @PathVariable int chunk,
@ApiParam("total chunks") @PathVariable int chunks,
@ApiParam("file location where chunk starts") @PathVariable long chunkStart,
@ApiParam("The file location where the chunk ends") @PathVariable long chunkEnd) {
try {
log.info("Starting to check the md5[{}] of chunk [{}]-[{}], if it exists", chunk, chunks, blockMd5);
return ResponseEntity.ok(this.fileServiceImpl.checkChunk(md5, blockMd5, md5CheckLength, chunk, chunks, chunkStart, chunkEnd) ? 1 : 0);
} catch (Exception e) {
return new ResponseEntity(RestError.DATABASE_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Override
public boolean checkChunk(String md5, String blockMd5, long md5CheckLength, int chunk, int chunks, long chunkStart, long chunkEnd) {
boolean isExist = false;
File chunkFile = new File(fileDir, TEMP_DIR + File.separator + md5 + File.separator + chunk + FILE_SEPARATOR + chunks + FILE_EXT);
if (chunkFile.exists() && chunkFile.length() == (chunkEnd - chunkStart)) {
String calBlockMd5 = FileUtils.md5(chunkFile, 0, md5CheckLength);
if (blockMd5.equals(calBlockMd5)) {
isExist = true;
}
}
log.info("{} of {}-{} chunk {} exists", md5, chunk, chunks, isExist ? "" : "no");
return isExist;
}
/**
* Merge files
*
* @param fileInfo
* @return
*/
@ApiOperation(value = "Merge files", notes = "Merge split uploads into one file")
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
@PostMapping(value = "mergeChunks")
public ResponseEntity<Integer> mergeChunks(@Validated @RequestBody FileInfo fileInfo, BindingResult bindingResult) {
log.info("Started merging files");
if (bindingResult.hasErrors()) {
log.error("Wrong parameter request");
return new ResponseEntity("Wrong parameter request", HttpStatus.BAD_REQUEST);
} else {
try {
DataEntity dataEntity = this.fileServiceImpl.mergeChunks(fileInfo);
log.info("Merge file completed, saved dataEntityId is:{}", dataEntity ! = null ? dataEntity.getId() : null);
return ResponseEntity.ok(dataEntity ! = null ? 1 : 0);
} catch (FileMargeException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.FILE_MARGE_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (FileNotAllException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.FILE_NOTALL_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (IOException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.IO_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
/**
* Merge files
*
* @param fileInfo
* @return {DataEntity}
* @throws FileNotAllException
* @throws IOException
*/
@Override
public DataEntity mergeChunks(FileInfo fileInfo) throws IOException, FileNotAllException, FileMargeException {
// First check if there is a file record in the library
Optional<UploadFileInfo> uploadFileInfoOptional = this.uploadFileDao.findByMd5AndSize(fileInfo.getMd5(), fileInfo.getSize());
log.info("Check if the file information exists in the database");
UploadFileInfo uploadFileInfo = null;
if (uploadFileInfoOptional.isPresent()) {
log.info("File info:{}", fileInfo);
uploadFileInfo = uploadFileInfoOptional.get();
}
if (uploadFileInfo == null) {
uploadFileInfo = new UploadFileInfo();
}
// check again if the file exists
log.info("Checking for real files");
File wholeFile = new File(getRealFileRoot(), fileInfo.getMd5() + FILE_SEPARATOR + fileInfo.getName());
if (!wholeFile.exists() || wholeFile.length() ! = fileInfo.getSize()) {
log.info("File does not exist or file length does not match! }");
if (wholeFile.exists()) {
log.info("Length is {}! ={},", wholeFile.length(), fileInfo.getSize());
}
File tempDirFile = new File(fileDir, TEMP_DIR + File.separator + fileInfo.getMd5());
try {
if (tempDirFile.exists()) {
log.info("File fragmentation directory exists");
// Get all the fragmented files in this directory
File[] partFiles = tempDirFile.listFiles((f, name) -> name.endsWith(FILE_EXT));
log.info("The number of file partitions is:", partFiles.length);
if (partFiles.length > 0) {
Arrays.sort(partFiles, (File f1, File f2) -> {
String name1 = f1.getName();
} else if (name1.length() > name2.length()) {
return 1;
} else {
return name1.compareTo(name2);
}
});
long size = 0;
FileChannel resultFileChannel = new FileOutputStream(wholeFile, true).getChannel();
for (int i = 0; i < partFiles.length; i++) {
size += partFiles[i].length();
if (size > wholeFile.length()) {
log.info("Merging the files in block {}{}", i, partFiles[i].getName());
// FileUtils.copy(partFiles[i], wholeFile, size);
FileChannel inChannel = new FileInputStream(partFiles[i]).getChannel();
resultFileChannel.transferFrom(inChannel, resultFileChannel.size(), inChannel.size());
inChannel.close();
}
}
if (size < wholeFile.length()) {
log.info("Slice file incomplete");
throw new FileNotAllException();
}
}
log.info("Deleting shard data information");
this.threadPoolUtil.getExecutor().execute(() -> {
tempDirFile.listFiles(child -> child.delete());
tempDirFile.delete();
});
}
} catch (Exception e) {
throw new FileMargeException();
}
}
if (uploadFileInfo.getId() == null) {
log.info("Saved uploaded file information");
uploadFileInfo.setCreateTime(fileInfo.getCreateTime());
uploadFileInfo.setMd5(fileInfo.getMd5());
uploadFileInfo.setType(fileInfo.getType());
uploadFileInfo.setSize(wholeFile.length());
uploadFileInfo.setDfsPath(wholeFile.getAbsolutePath().substring(this.fileDir.length() + 1));
this.uploadFileDao.save(uploadFileInfo);
}
// File size, should be updated when the merge is complete
log.info("Get parent directory information");
DataEntity parent = this.getDataEntityById(fileInfo.getParentId());
// If the file information contains the relative path of the file, the real directory where the file will be uploaded should be created
String path = fileInfo.getPath();
if (StringUtils.hasText(path)) {
log.info("Include relative directory, create relative directory");
path = FilenameUtils.getFullPathNoEndSeparator(path);
String[] paths = path.split("/");
for (String tempPath : paths) {
if (StringUtils.hasText(tempPath)) { if (StringUtils.hasText(tempPath)) {
DataEntity dataEntity = this.dataEntityDao.findByNameAndParentAndUserId(tempPath, parent, UserUtil.getUserId());
if (dataEntity == null) {
dataEntity = new DataEntity();
dataEntity.setName(tempPath);
dataEntity.setDir(true);
dataEntity.setParent(parent);
parent = this.dataEntityDao.save(dataEntity);
} else {
parent = dataEntity;
}
}
}
}
log.info("Creating directory information");
DataEntity dataEntity = new DataEntity();
dataEntity.setName(fileInfo.getName());
dataEntity.setExt(fileInfo.getExt());
dataEntity.setDat
スライスが存在するかどうかをチェックする
/**
* Check if the slice exists
*
* @param md5
* @param chunk
* @param chunks
* @param chunkStart
* @param chunkEnd
* @return
*/
@ApiOperation(value = "Check if chunk exists")
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
@GetMapping(value = "checkChunk/{md5}/{blockMd5}/{md5CheckLength}/{chunk}/{chunks}/{chunkStart}/{chunkEnd}")
public ResponseEntity<Integer> checkChunk(@ApiParam("file md5 code") @PathVariable String md5,
@ApiParam("chunked file md5 code") @PathVariable String blockMd5,
@ApiParam("used to check the length of the block file md5 code") @PathVariable long md5CheckLength,
@ApiParam("how many chunks, starting from 0") @PathVariable int chunk,
@ApiParam("total chunks") @PathVariable int chunks,
@ApiParam("file location where chunk starts") @PathVariable long chunkStart,
@ApiParam("The file location where the chunk ends") @PathVariable long chunkEnd) {
try {
log.info("Starting to check the md5[{}] of chunk [{}]-[{}], if it exists", chunk, chunks, blockMd5);
return ResponseEntity.ok(this.fileServiceImpl.checkChunk(md5, blockMd5, md5CheckLength, chunk, chunks, chunkStart, chunkEnd) ? 1 : 0);
} catch (Exception e) {
return new ResponseEntity(RestError.DATABASE_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Override
public boolean checkChunk(String md5, String blockMd5, long md5CheckLength, int chunk, int chunks, long chunkStart, long chunkEnd) {
boolean isExist = false;
File chunkFile = new File(fileDir, TEMP_DIR + File.separator + md5 + File.separator + chunk + FILE_SEPARATOR + chunks + FILE_EXT);
if (chunkFile.exists() && chunkFile.length() == (chunkEnd - chunkStart)) {
String calBlockMd5 = FileUtils.md5(chunkFile, 0, md5CheckLength);
if (blockMd5.equals(calBlockMd5)) {
isExist = true;
}
}
log.info("{} of {}-{} chunk {} exists", md5, chunk, chunks, isExist ? "" : "no");
return isExist;
}
ファイルをマージする
/**
* Merge files
*
* @param fileInfo
* @return
*/
@ApiOperation(value = "Merge files", notes = "Merge split uploads into one file")
@ApiResponses({
@ApiResponse(code = 500, response = RestError.class, message = "error")
})
@PostMapping(value = "mergeChunks")
public ResponseEntity<Integer> mergeChunks(@Validated @RequestBody FileInfo fileInfo, BindingResult bindingResult) {
log.info("Started merging files");
if (bindingResult.hasErrors()) {
log.error("Wrong parameter request");
return new ResponseEntity("Wrong parameter request", HttpStatus.BAD_REQUEST);
} else {
try {
DataEntity dataEntity = this.fileServiceImpl.mergeChunks(fileInfo);
log.info("Merge file completed, saved dataEntityId is:{}", dataEntity ! = null ? dataEntity.getId() : null);
return ResponseEntity.ok(dataEntity ! = null ? 1 : 0);
} catch (FileMargeException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.FILE_MARGE_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (FileNotAllException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.FILE_NOTALL_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (IOException e) {
log.error(e.getMessage(), e);
return new ResponseEntity(RestError.IO_ERROR.setReason(e.getMessage()).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
/**
* Merge files
*
* @param fileInfo
* @return {DataEntity}
* @throws FileNotAllException
* @throws IOException
*/
@Override
public DataEntity mergeChunks(FileInfo fileInfo) throws IOException, FileNotAllException, FileMargeException {
// First check if there is a file record in the library
Optional<UploadFileInfo> uploadFileInfoOptional = this.uploadFileDao.findByMd5AndSize(fileInfo.getMd5(), fileInfo.getSize());
log.info("Check if the file information exists in the database");
UploadFileInfo uploadFileInfo = null;
if (uploadFileInfoOptional.isPresent()) {
log.info("File info:{}", fileInfo);
uploadFileInfo = uploadFileInfoOptional.get();
}
if (uploadFileInfo == null) {
uploadFileInfo = new UploadFileInfo();
}
// check again if the file exists
log.info("Checking for real files");
File wholeFile = new File(getRealFileRoot(), fileInfo.getMd5() + FILE_SEPARATOR + fileInfo.getName());
if (!wholeFile.exists() || wholeFile.length() ! = fileInfo.getSize()) {
log.info("File does not exist or file length does not match! }");
if (wholeFile.exists()) {
log.info("Length is {}! ={},", wholeFile.length(), fileInfo.getSize());
}
File tempDirFile = new File(fileDir, TEMP_DIR + File.separator + fileInfo.getMd5());
try {
if (tempDirFile.exists()) {
log.info("File fragmentation directory exists");
// Get all the fragmented files in this directory
File[] partFiles = tempDirFile.listFiles((f, name) -> name.endsWith(FILE_EXT));
log.info("The number of file partitions is:", partFiles.length);
if (partFiles.length > 0) {
Arrays.sort(partFiles, (File f1, File f2) -> {
String name1 = f1.getName();
} else if (name1.length() > name2.length()) {
return 1;
} else {
return name1.compareTo(name2);
}
});
long size = 0;
FileChannel resultFileChannel = new FileOutputStream(wholeFile, true).getChannel();
for (int i = 0; i < partFiles.length; i++) {
size += partFiles[i].length();
if (size > wholeFile.length()) {
log.info("Merging the files in block {}{}", i, partFiles[i].getName());
// FileUtils.copy(partFiles[i], wholeFile, size);
FileChannel inChannel = new FileInputStream(partFiles[i]).getChannel();
resultFileChannel.transferFrom(inChannel, resultFileChannel.size(), inChannel.size());
inChannel.close();
}
}
if (size < wholeFile.length()) {
log.info("Slice file incomplete");
throw new FileNotAllException();
}
}
log.info("Deleting shard data information");
this.threadPoolUtil.getExecutor().execute(() -> {
tempDirFile.listFiles(child -> child.delete());
tempDirFile.delete();
});
}
} catch (Exception e) {
throw new FileMargeException();
}
}
if (uploadFileInfo.getId() == null) {
log.info("Saved uploaded file information");
uploadFileInfo.setCreateTime(fileInfo.getCreateTime());
uploadFileInfo.setMd5(fileInfo.getMd5());
uploadFileInfo.setType(fileInfo.getType());
uploadFileInfo.setSize(wholeFile.length());
uploadFileInfo.setDfsPath(wholeFile.getAbsolutePath().substring(this.fileDir.length() + 1));
this.uploadFileDao.save(uploadFileInfo);
}
// File size, should be updated when the merge is complete
log.info("Get parent directory information");
DataEntity parent = this.getDataEntityById(fileInfo.getParentId());
// If the file information contains the relative path of the file, the real directory where the file will be uploaded should be created
String path = fileInfo.getPath();
if (StringUtils.hasText(path)) {
log.info("Include relative directory, create relative directory");
path = FilenameUtils.getFullPathNoEndSeparator(path);
String[] paths = path.split("/");
for (String tempPath : paths) {
if (StringUtils.hasText(tempPath)) { if (StringUtils.hasText(tempPath)) {
DataEntity dataEntity = this.dataEntityDao.findByNameAndParentAndUserId(tempPath, parent, UserUtil.getUserId());
if (dataEntity == null) {
dataEntity = new DataEntity();
dataEntity.setName(tempPath);
dataEntity.setDir(true);
dataEntity.setParent(parent);
parent = this.dataEntityDao.save(dataEntity);
} else {
parent = dataEntity;
}
}
}
}
log.info("Creating directory information");
DataEntity dataEntity = new DataEntity();
dataEntity.setName(fileInfo.getName());
dataEntity.setExt(fileInfo.getExt());
dataEntity.setDat
セカンドパス機能の原理は、ファイルのMD5をチェックし、ファイルをアップロードする前にファイルの内容またはMD5値の一部を取得し、同じMD5がすでに自分のレコードに存在するかどうかを調べ、存在する場合は、アップロードを再分割せずにサーバーの実パスから直接取得し、セカンドパスとしての効果を実現するものである。
関連
-
undefinedeclipse エラー。この行に複数のアノテーションが見つかりました: - 文字列を型解決に解決できない
-
スキャナは、タイプに解決することはできません最もルーキー初心者の質問
-
プロローグでのコンテンツは禁止されています
-
JQuery DataTable 详解
-
javaでよく使われる英単語
-
HttpClientがGZIP形式でない場合の対処法
-
JSPで「リストが型解決できない!」の解決方法
-
コミットには何も追加されないが、未追跡のファイルが存在し、gitで未追跡のファイルに対する完璧な解決策
-
トークン "{" のシンタックス エラー、このトークンの後に { があるはずです。
-
エコー文字列を決定するためのjavaの簡単な実装をいくつか紹介します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java エラー報告 スレッド "main" での例外 java.util.NoSuchElementException
-
Android Studio 3.1.2 で v4, v7 パッケージが見つからない シンボル 'AppCompatActivity' を解決できない
-
Spring boot runs with Error creating bean with name 'entityManagerFactory' defined in class path resource
-
node js npm gruntインストール、elasticsearch-head 5.Xインストール
-
マスキング このリソースにアクセスするには、完全な認証が必要です。
-
アイデア Springboot Web プロジェクトを jar にパッケージ化する場合、Error: 無効または破損した jarfile x.jar 解決策
-
Error: java.lang.NoClassDefFoundError: クラス XXXX を初期化できませんでした
-
スレッド "main" で例外発生 java.net.BindException: アドレスは既に使用中です。NET_Bind
-
IDEA パッケージステートメントの欠落
-
同期・並行クラスコンテナ